btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / kits / interface / ScrollBar.cpp
blob8049b990c2c8b9d3c5c1c3177f6c4191eb9723ab
1 /*
2 * Copyright 2001-2014 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
5 * Authors:
6 * Stephan Aßmus, superstippi@gmx.de
7 * Stefano Ceccherini, burton666@libero.it
8 * DarkWyrm, bpmagic@columbus.rr.com
9 * Marc Flerackers, mflerackers@androme.be
10 * John Scipione, jscipione@gmail.com
14 #include <ScrollBar.h>
16 #include <math.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
21 #include <ControlLook.h>
22 #include <LayoutUtils.h>
23 #include <Message.h>
24 #include <OS.h>
25 #include <Shape.h>
26 #include <Window.h>
28 #include <binary_compatibility/Interface.h>
31 //#define TRACE_SCROLLBAR
32 #ifdef TRACE_SCROLLBAR
33 # define TRACE(x...) printf(x)
34 #else
35 # define TRACE(x...)
36 #endif
39 typedef enum {
40 ARROW_LEFT = 0,
41 ARROW_RIGHT,
42 ARROW_UP,
43 ARROW_DOWN,
44 ARROW_NONE
45 } arrow_direction;
48 #define SBC_SCROLLBYVALUE 0
49 #define SBC_SETDOUBLE 1
50 #define SBC_SETPROPORTIONAL 2
51 #define SBC_SETSTYLE 3
53 // Quick constants for determining which arrow is down and are defined with
54 // respect to double arrow mode. ARROW1 and ARROW4 refer to the outer pair of
55 // arrows and ARROW2 and ARROW3 refer to the inner ones. ARROW1 points left/up
56 // and ARROW4 points right/down.
57 #define ARROW1 0
58 #define ARROW2 1
59 #define ARROW3 2
60 #define ARROW4 3
61 #define THUMB 4
62 #define NOARROW -1
65 static const bigtime_t kRepeatDelay = 300000;
68 // Because the R5 version kept a lot of data on server-side, we need to kludge
69 // our way into binary compatibility
70 class BScrollBar::Private {
71 public:
72 Private(BScrollBar* scrollBar)
74 fScrollBar(scrollBar),
75 fEnabled(true),
76 fRepeaterThread(-1),
77 fExitRepeater(false),
78 fRepeaterDelay(0),
79 fThumbFrame(0.0, 0.0, -1.0, -1.0),
80 fDoRepeat(false),
81 fClickOffset(0.0, 0.0),
82 fThumbInc(0.0),
83 fStopValue(0.0),
84 fUpArrowsEnabled(true),
85 fDownArrowsEnabled(true),
86 fBorderHighlighted(false),
87 fButtonDown(NOARROW)
89 #ifdef TEST_MODE
90 fScrollBarInfo.proportional = true;
91 fScrollBarInfo.double_arrows = true;
92 fScrollBarInfo.knob = 0;
93 fScrollBarInfo.min_knob_size = 15;
94 #else
95 get_scroll_bar_info(&fScrollBarInfo);
96 #endif
99 ~Private()
101 if (fRepeaterThread >= 0) {
102 status_t dummy;
103 fExitRepeater = true;
104 wait_for_thread(fRepeaterThread, &dummy);
108 void DrawScrollBarButton(BScrollBar* owner, arrow_direction direction,
109 BRect frame, bool down = false);
111 static int32 button_repeater_thread(void* data);
113 int32 ButtonRepeaterThread();
115 BScrollBar* fScrollBar;
116 bool fEnabled;
118 // TODO: This should be a static, initialized by
119 // _init_interface_kit() at application startup-time,
120 // like BMenu::sMenuInfo
121 scroll_bar_info fScrollBarInfo;
123 thread_id fRepeaterThread;
124 volatile bool fExitRepeater;
125 bigtime_t fRepeaterDelay;
127 BRect fThumbFrame;
128 volatile bool fDoRepeat;
129 BPoint fClickOffset;
131 float fThumbInc;
132 float fStopValue;
134 bool fUpArrowsEnabled;
135 bool fDownArrowsEnabled;
137 bool fBorderHighlighted;
139 int8 fButtonDown;
143 // This thread is spawned when a button is initially pushed and repeatedly scrolls
144 // the scrollbar by a little bit after a short delay
145 int32
146 BScrollBar::Private::button_repeater_thread(void* data)
148 BScrollBar::Private* privateData = (BScrollBar::Private*)data;
149 return privateData->ButtonRepeaterThread();
153 int32
154 BScrollBar::Private::ButtonRepeaterThread()
156 // Wait a bit before auto scrolling starts. As long as the user releases
157 // and presses the button again while the repeat delay has not yet
158 // triggered, the value is pushed into the future, so we need to loop such
159 // that repeating starts at exactly the correct delay after the last
160 // button press.
161 while (fRepeaterDelay > system_time() && !fExitRepeater)
162 snooze_until(fRepeaterDelay, B_SYSTEM_TIMEBASE);
164 // repeat loop
165 while (!fExitRepeater) {
166 if (fScrollBar->LockLooper()) {
167 if (fDoRepeat) {
168 float value = fScrollBar->Value() + fThumbInc;
169 if (fButtonDown == NOARROW) {
170 // in this case we want to stop when we're under the mouse
171 if (fThumbInc > 0.0 && value <= fStopValue)
172 fScrollBar->SetValue(value);
173 if (fThumbInc < 0.0 && value >= fStopValue)
174 fScrollBar->SetValue(value);
175 } else
176 fScrollBar->SetValue(value);
179 fScrollBar->UnlockLooper();
182 snooze(25000);
185 // tell scrollbar we're gone
186 if (fScrollBar->LockLooper()) {
187 fRepeaterThread = -1;
188 fScrollBar->UnlockLooper();
191 return 0;
195 // #pragma mark - BScrollBar
198 BScrollBar::BScrollBar(BRect frame, const char* name, BView* target,
199 float min, float max, orientation direction)
201 BView(frame, name, B_FOLLOW_NONE,
202 B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
203 fMin(min),
204 fMax(max),
205 fSmallStep(1.0f),
206 fLargeStep(10.0f),
207 fValue(0),
208 fProportion(0.0f),
209 fTarget(NULL),
210 fOrientation(direction)
212 SetViewColor(B_TRANSPARENT_COLOR);
214 fPrivateData = new BScrollBar::Private(this);
216 SetTarget(target);
217 SetEventMask(B_NO_POINTER_HISTORY);
219 _UpdateThumbFrame();
220 _UpdateArrowButtons();
222 SetResizingMode(direction == B_VERTICAL
223 ? B_FOLLOW_TOP_BOTTOM | B_FOLLOW_RIGHT
224 : B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
228 BScrollBar::BScrollBar(const char* name, BView* target,
229 float min, float max, orientation direction)
231 BView(name, B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS),
232 fMin(min),
233 fMax(max),
234 fSmallStep(1.0f),
235 fLargeStep(10.0f),
236 fValue(0),
237 fProportion(0.0f),
238 fTarget(NULL),
239 fOrientation(direction)
241 SetViewColor(B_TRANSPARENT_COLOR);
243 fPrivateData = new BScrollBar::Private(this);
245 SetTarget(target);
246 SetEventMask(B_NO_POINTER_HISTORY);
248 _UpdateThumbFrame();
249 _UpdateArrowButtons();
253 BScrollBar::BScrollBar(BMessage* data)
255 BView(data),
256 fTarget(NULL)
258 fPrivateData = new BScrollBar::Private(this);
260 // TODO: Does the BeOS implementation try to find the target
261 // by name again? Does it archive the name at all?
262 if (data->FindFloat("_range", 0, &fMin) < B_OK)
263 fMin = 0.0f;
265 if (data->FindFloat("_range", 1, &fMax) < B_OK)
266 fMax = 0.0f;
268 if (data->FindFloat("_steps", 0, &fSmallStep) < B_OK)
269 fSmallStep = 1.0f;
271 if (data->FindFloat("_steps", 1, &fLargeStep) < B_OK)
272 fLargeStep = 10.0f;
274 if (data->FindFloat("_val", &fValue) < B_OK)
275 fValue = 0.0;
277 int32 orientation;
278 if (data->FindInt32("_orient", &orientation) < B_OK) {
279 fOrientation = B_VERTICAL;
280 if ((Flags() & B_SUPPORTS_LAYOUT) == 0) {
281 // just to make sure
282 SetResizingMode(fOrientation == B_VERTICAL
283 ? B_FOLLOW_TOP_BOTTOM | B_FOLLOW_RIGHT
284 : B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM);
286 } else
287 fOrientation = (enum orientation)orientation;
289 if (data->FindFloat("_prop", &fProportion) < B_OK)
290 fProportion = 0.0;
292 _UpdateThumbFrame();
293 _UpdateArrowButtons();
297 BScrollBar::~BScrollBar()
299 SetTarget((BView*)NULL);
300 delete fPrivateData;
304 BArchivable*
305 BScrollBar::Instantiate(BMessage* data)
307 if (validate_instantiation(data, "BScrollBar"))
308 return new BScrollBar(data);
309 return NULL;
313 status_t
314 BScrollBar::Archive(BMessage* data, bool deep) const
316 status_t err = BView::Archive(data, deep);
317 if (err != B_OK)
318 return err;
320 err = data->AddFloat("_range", fMin);
321 if (err != B_OK)
322 return err;
324 err = data->AddFloat("_range", fMax);
325 if (err != B_OK)
326 return err;
328 err = data->AddFloat("_steps", fSmallStep);
329 if (err != B_OK)
330 return err;
332 err = data->AddFloat("_steps", fLargeStep);
333 if (err != B_OK)
334 return err;
336 err = data->AddFloat("_val", fValue);
337 if (err != B_OK)
338 return err;
340 err = data->AddInt32("_orient", (int32)fOrientation);
341 if (err != B_OK)
342 return err;
344 err = data->AddFloat("_prop", fProportion);
346 return err;
350 void
351 BScrollBar::AllAttached()
353 BView::AllAttached();
357 void
358 BScrollBar::AllDetached()
360 BView::AllDetached();
364 void
365 BScrollBar::AttachedToWindow()
367 BView::AttachedToWindow();
371 void
372 BScrollBar::DetachedFromWindow()
374 BView::DetachedFromWindow();
378 void
379 BScrollBar::Draw(BRect updateRect)
381 BRect bounds = Bounds();
383 rgb_color normal = ui_color(B_PANEL_BACKGROUND_COLOR);
385 // stroke a dark frame around the entire scrollbar
386 // (independent of enabled state)
387 // take care of border highlighting (scroll target is focus view)
388 SetHighColor(tint_color(normal, B_DARKEN_2_TINT));
389 if (fPrivateData->fBorderHighlighted && fPrivateData->fEnabled) {
390 rgb_color borderColor = HighColor();
391 rgb_color highlightColor = ui_color(B_KEYBOARD_NAVIGATION_COLOR);
392 BeginLineArray(4);
393 AddLine(BPoint(bounds.left + 1, bounds.bottom),
394 BPoint(bounds.right, bounds.bottom), borderColor);
395 AddLine(BPoint(bounds.right, bounds.top + 1),
396 BPoint(bounds.right, bounds.bottom - 1), borderColor);
397 if (fOrientation == B_HORIZONTAL) {
398 AddLine(BPoint(bounds.left, bounds.top + 1),
399 BPoint(bounds.left, bounds.bottom), borderColor);
400 } else {
401 AddLine(BPoint(bounds.left, bounds.top),
402 BPoint(bounds.left, bounds.bottom), highlightColor);
404 if (fOrientation == B_HORIZONTAL) {
405 AddLine(BPoint(bounds.left, bounds.top),
406 BPoint(bounds.right, bounds.top), highlightColor);
407 } else {
408 AddLine(BPoint(bounds.left + 1, bounds.top),
409 BPoint(bounds.right, bounds.top), borderColor);
411 EndLineArray();
412 } else
413 StrokeRect(bounds);
415 bounds.InsetBy(1.0f, 1.0f);
417 bool enabled = fPrivateData->fEnabled && fMin < fMax
418 && fProportion < 1.0f && fProportion >= 0.0f;
420 rgb_color light, dark, dark1, dark2;
421 if (enabled) {
422 light = tint_color(normal, B_LIGHTEN_MAX_TINT);
423 dark = tint_color(normal, B_DARKEN_3_TINT);
424 dark1 = tint_color(normal, B_DARKEN_1_TINT);
425 dark2 = tint_color(normal, B_DARKEN_2_TINT);
426 } else {
427 light = tint_color(normal, B_LIGHTEN_MAX_TINT);
428 dark = tint_color(normal, B_DARKEN_2_TINT);
429 dark1 = tint_color(normal, B_LIGHTEN_2_TINT);
430 dark2 = tint_color(normal, B_LIGHTEN_1_TINT);
433 SetDrawingMode(B_OP_OVER);
435 BRect thumbBG = bounds;
436 bool doubleArrows = _DoubleArrows();
438 // Draw arrows
439 if (fOrientation == B_HORIZONTAL) {
440 BRect buttonFrame(bounds.left, bounds.top,
441 bounds.left + bounds.Height(), bounds.bottom);
443 _DrawArrowButton(ARROW_LEFT, doubleArrows, buttonFrame, updateRect,
444 enabled, fPrivateData->fButtonDown == ARROW1);
446 if (doubleArrows) {
447 buttonFrame.OffsetBy(bounds.Height() + 1, 0.0f);
448 _DrawArrowButton(ARROW_RIGHT, doubleArrows, buttonFrame, updateRect,
449 enabled, fPrivateData->fButtonDown == ARROW2);
451 buttonFrame.OffsetTo(bounds.right - ((bounds.Height() * 2) + 1),
452 bounds.top);
453 _DrawArrowButton(ARROW_LEFT, doubleArrows, buttonFrame, updateRect,
454 enabled, fPrivateData->fButtonDown == ARROW3);
456 thumbBG.left += bounds.Height() * 2 + 2;
457 thumbBG.right -= bounds.Height() * 2 + 2;
458 } else {
459 thumbBG.left += bounds.Height() + 1;
460 thumbBG.right -= bounds.Height() + 1;
463 buttonFrame.OffsetTo(bounds.right - bounds.Height(), bounds.top);
464 _DrawArrowButton(ARROW_RIGHT, doubleArrows, buttonFrame, updateRect,
465 enabled, fPrivateData->fButtonDown == ARROW4);
466 } else {
467 BRect buttonFrame(bounds.left, bounds.top, bounds.right,
468 bounds.top + bounds.Width());
470 _DrawArrowButton(ARROW_UP, doubleArrows, buttonFrame, updateRect,
471 enabled, fPrivateData->fButtonDown == ARROW1);
473 if (doubleArrows) {
474 buttonFrame.OffsetBy(0.0f, bounds.Width() + 1);
475 _DrawArrowButton(ARROW_DOWN, doubleArrows, buttonFrame, updateRect,
476 enabled, fPrivateData->fButtonDown == ARROW2);
478 buttonFrame.OffsetTo(bounds.left, bounds.bottom
479 - ((bounds.Width() * 2) + 1));
480 _DrawArrowButton(ARROW_UP, doubleArrows, buttonFrame, updateRect,
481 enabled, fPrivateData->fButtonDown == ARROW3);
483 thumbBG.top += bounds.Width() * 2 + 2;
484 thumbBG.bottom -= bounds.Width() * 2 + 2;
485 } else {
486 thumbBG.top += bounds.Width() + 1;
487 thumbBG.bottom -= bounds.Width() + 1;
490 buttonFrame.OffsetTo(bounds.left, bounds.bottom - bounds.Width());
491 _DrawArrowButton(ARROW_DOWN, doubleArrows, buttonFrame, updateRect,
492 enabled, fPrivateData->fButtonDown == ARROW4);
495 SetDrawingMode(B_OP_COPY);
497 // background for thumb area
498 BRect rect(fPrivateData->fThumbFrame);
500 SetHighColor(dark1);
502 uint32 flags = 0;
503 if (!enabled)
504 flags |= BControlLook::B_DISABLED;
506 // fill background besides the thumb
507 if (fOrientation == B_HORIZONTAL) {
508 BRect leftOfThumb(thumbBG.left, thumbBG.top, rect.left - 1,
509 thumbBG.bottom);
510 BRect rightOfThumb(rect.right + 1, thumbBG.top, thumbBG.right,
511 thumbBG.bottom);
513 be_control_look->DrawScrollBarBackground(this, leftOfThumb,
514 rightOfThumb, updateRect, normal, flags, fOrientation);
515 } else {
516 BRect topOfThumb(thumbBG.left, thumbBG.top,
517 thumbBG.right, rect.top - 1);
519 BRect bottomOfThumb(thumbBG.left, rect.bottom + 1,
520 thumbBG.right, thumbBG.bottom);
522 be_control_look->DrawScrollBarBackground(this, topOfThumb,
523 bottomOfThumb, updateRect, normal, flags, fOrientation);
526 rgb_color thumbColor = ui_color(B_SCROLL_BAR_THUMB_COLOR);
528 // Draw scroll thumb
529 if (enabled) {
530 // fill the clickable surface of the thumb
531 be_control_look->DrawButtonBackground(this, rect, updateRect,
532 thumbColor, 0, BControlLook::B_ALL_BORDERS, fOrientation);
533 // TODO: Add the other thumb styles - dots and lines
534 } else {
535 if (fMin >= fMax || fProportion >= 1.0f || fProportion < 0.0f) {
536 // we cannot scroll at all
537 _DrawDisabledBackground(thumbBG, light, dark, dark1);
538 } else {
539 // we could scroll, but we're simply disabled
540 float bgTint = 1.06;
541 rgb_color bgLight = tint_color(light, bgTint * 3);
542 rgb_color bgShadow = tint_color(dark, bgTint);
543 rgb_color bgFill = tint_color(dark1, bgTint);
544 if (fOrientation == B_HORIZONTAL) {
545 // left of thumb
546 BRect besidesThumb(thumbBG);
547 besidesThumb.right = rect.left - 1;
548 _DrawDisabledBackground(besidesThumb, bgLight, bgShadow, bgFill);
549 // right of thumb
550 besidesThumb.left = rect.right + 1;
551 besidesThumb.right = thumbBG.right;
552 _DrawDisabledBackground(besidesThumb, bgLight, bgShadow, bgFill);
553 } else {
554 // above thumb
555 BRect besidesThumb(thumbBG);
556 besidesThumb.bottom = rect.top - 1;
557 _DrawDisabledBackground(besidesThumb, bgLight, bgShadow, bgFill);
558 // below thumb
559 besidesThumb.top = rect.bottom + 1;
560 besidesThumb.bottom = thumbBG.bottom;
561 _DrawDisabledBackground(besidesThumb, bgLight, bgShadow, bgFill);
563 // thumb bevel
564 BeginLineArray(4);
565 AddLine(BPoint(rect.left, rect.bottom),
566 BPoint(rect.left, rect.top), light);
567 AddLine(BPoint(rect.left + 1, rect.top),
568 BPoint(rect.right, rect.top), light);
569 AddLine(BPoint(rect.right, rect.top + 1),
570 BPoint(rect.right, rect.bottom), dark2);
571 AddLine(BPoint(rect.right - 1, rect.bottom),
572 BPoint(rect.left + 1, rect.bottom), dark2);
573 EndLineArray();
574 // thumb fill
575 rect.InsetBy(1.0, 1.0);
576 SetHighColor(dark1);
577 FillRect(rect);
583 void
584 BScrollBar::FrameMoved(BPoint newPosition)
586 BView::FrameMoved(newPosition);
590 void
591 BScrollBar::FrameResized(float newWidth, float newHeight)
593 _UpdateThumbFrame();
597 void
598 BScrollBar::MessageReceived(BMessage* message)
600 switch(message->what) {
601 case B_VALUE_CHANGED:
603 int32 value;
604 if (message->FindInt32("value", &value) == B_OK)
605 ValueChanged(value);
607 break;
610 case B_MOUSE_WHEEL_CHANGED:
612 // Must handle this here since BView checks for the existence of
613 // scrollbars, which a scrollbar itself does not have
614 float deltaX = 0.0f;
615 float deltaY = 0.0f;
616 message->FindFloat("be:wheel_delta_x", &deltaX);
617 message->FindFloat("be:wheel_delta_y", &deltaY);
619 if (deltaX == 0.0f && deltaY == 0.0f)
620 break;
622 if (deltaX != 0.0f && deltaY == 0.0f)
623 deltaY = deltaX;
625 ScrollWithMouseWheelDelta(this, deltaY);
628 default:
629 BView::MessageReceived(message);
634 void
635 BScrollBar::MouseDown(BPoint where)
637 if (!fPrivateData->fEnabled || fMin == fMax)
638 return;
640 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
642 int32 buttons;
643 if (Looper() == NULL || Looper()->CurrentMessage() == NULL
644 || Looper()->CurrentMessage()->FindInt32("buttons", &buttons) != B_OK) {
645 buttons = B_PRIMARY_MOUSE_BUTTON;
648 if (buttons & B_SECONDARY_MOUSE_BUTTON) {
649 // special absolute scrolling: move thumb to where we clicked
650 fPrivateData->fButtonDown = THUMB;
651 fPrivateData->fClickOffset = fPrivateData->fThumbFrame.LeftTop() - where;
652 if (Orientation() == B_HORIZONTAL)
653 fPrivateData->fClickOffset.x = -fPrivateData->fThumbFrame.Width() / 2;
654 else
655 fPrivateData->fClickOffset.y = -fPrivateData->fThumbFrame.Height() / 2;
657 SetValue(_ValueFor(where + fPrivateData->fClickOffset));
658 return;
661 // hit test for the thumb
662 if (fPrivateData->fThumbFrame.Contains(where)) {
663 fPrivateData->fButtonDown = THUMB;
664 fPrivateData->fClickOffset = fPrivateData->fThumbFrame.LeftTop() - where;
665 Invalidate(fPrivateData->fThumbFrame);
666 return;
669 // hit test for arrows or empty area
670 float scrollValue = 0.0;
672 // pressing the shift key scrolls faster
673 float buttonStepSize
674 = (modifiers() & B_SHIFT_KEY) != 0 ? fLargeStep : fSmallStep;
676 fPrivateData->fButtonDown = _ButtonFor(where);
677 switch (fPrivateData->fButtonDown) {
678 case ARROW1:
679 scrollValue = -buttonStepSize;
680 break;
682 case ARROW2:
683 scrollValue = buttonStepSize;
684 break;
686 case ARROW3:
687 scrollValue = -buttonStepSize;
688 break;
690 case ARROW4:
691 scrollValue = buttonStepSize;
692 break;
694 case NOARROW:
695 // we hit the empty area, figure out which side of the thumb
696 if (fOrientation == B_VERTICAL) {
697 if (where.y < fPrivateData->fThumbFrame.top)
698 scrollValue = -fLargeStep;
699 else
700 scrollValue = fLargeStep;
701 } else {
702 if (where.x < fPrivateData->fThumbFrame.left)
703 scrollValue = -fLargeStep;
704 else
705 scrollValue = fLargeStep;
707 _UpdateTargetValue(where);
708 break;
710 if (scrollValue != 0.0) {
711 SetValue(fValue + scrollValue);
712 Invalidate(_ButtonRectFor(fPrivateData->fButtonDown));
714 // launch the repeat thread
715 if (fPrivateData->fRepeaterThread == -1) {
716 fPrivateData->fExitRepeater = false;
717 fPrivateData->fRepeaterDelay = system_time() + kRepeatDelay;
718 fPrivateData->fThumbInc = scrollValue;
719 fPrivateData->fDoRepeat = true;
720 fPrivateData->fRepeaterThread = spawn_thread(
721 fPrivateData->button_repeater_thread, "scroll repeater",
722 B_NORMAL_PRIORITY, fPrivateData);
723 resume_thread(fPrivateData->fRepeaterThread);
724 } else {
725 fPrivateData->fExitRepeater = false;
726 fPrivateData->fRepeaterDelay = system_time() + kRepeatDelay;
727 fPrivateData->fDoRepeat = true;
733 void
734 BScrollBar::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
736 if (!fPrivateData->fEnabled || fMin >= fMax || fProportion >= 1.0f
737 || fProportion < 0.0f) {
738 return;
741 if (fPrivateData->fButtonDown != NOARROW) {
742 if (fPrivateData->fButtonDown == THUMB) {
743 SetValue(_ValueFor(where + fPrivateData->fClickOffset));
744 } else {
745 // suspend the repeating if the mouse is not over the button
746 bool repeat = _ButtonRectFor(fPrivateData->fButtonDown).Contains(
747 where);
748 if (fPrivateData->fDoRepeat != repeat) {
749 fPrivateData->fDoRepeat = repeat;
750 Invalidate(_ButtonRectFor(fPrivateData->fButtonDown));
753 } else {
754 // update the value at which we want to stop repeating
755 if (fPrivateData->fDoRepeat) {
756 _UpdateTargetValue(where);
757 // we might have to turn arround
758 if ((fValue < fPrivateData->fStopValue
759 && fPrivateData->fThumbInc < 0)
760 || (fValue > fPrivateData->fStopValue
761 && fPrivateData->fThumbInc > 0)) {
762 fPrivateData->fThumbInc = -fPrivateData->fThumbInc;
769 void
770 BScrollBar::MouseUp(BPoint where)
772 if (fPrivateData->fButtonDown == THUMB)
773 Invalidate(fPrivateData->fThumbFrame);
774 else
775 Invalidate(_ButtonRectFor(fPrivateData->fButtonDown));
777 fPrivateData->fButtonDown = NOARROW;
778 fPrivateData->fExitRepeater = true;
779 fPrivateData->fDoRepeat = false;
783 #if DISABLES_ON_WINDOW_DEACTIVATION
784 void
785 BScrollBar::WindowActivated(bool active)
787 fPrivateData->fEnabled = active;
788 Invalidate();
790 #endif // DISABLES_ON_WINDOW_DEACTIVATION
793 void
794 BScrollBar::SetValue(float value)
796 if (value > fMax)
797 value = fMax;
798 else if (value < fMin)
799 value = fMin;
800 else if (isnan(value) || isinf(value))
801 return;
803 value = roundf(value);
804 if (value == fValue)
805 return;
807 TRACE("BScrollBar(%s)::SetValue(%.1f)\n", Name(), value);
809 fValue = value;
811 _UpdateThumbFrame();
812 _UpdateArrowButtons();
814 ValueChanged(fValue);
818 float
819 BScrollBar::Value() const
821 return fValue;
825 void
826 BScrollBar::ValueChanged(float newValue)
828 TRACE("BScrollBar(%s)::ValueChanged(%.1f)\n", Name(), newValue);
830 if (fTarget != NULL) {
831 // cache target bounds
832 BRect targetBounds = fTarget->Bounds();
833 // if vertical, check bounds top and scroll if different from newValue
834 if (fOrientation == B_VERTICAL && targetBounds.top != newValue)
835 fTarget->ScrollBy(0.0, newValue - targetBounds.top);
837 // if horizontal, check bounds left and scroll if different from newValue
838 if (fOrientation == B_HORIZONTAL && targetBounds.left != newValue)
839 fTarget->ScrollBy(newValue - targetBounds.left, 0.0);
842 TRACE(" -> %.1f\n", newValue);
844 SetValue(newValue);
848 void
849 BScrollBar::SetProportion(float value)
851 if (value < 0.0f)
852 value = 0.0f;
853 else if (value > 1.0f)
854 value = 1.0f;
856 if (value == fProportion)
857 return;
859 TRACE("BScrollBar(%s)::SetProportion(%.1f)\n", Name(), value);
861 bool oldEnabled = fPrivateData->fEnabled && fMin < fMax
862 && fProportion < 1.0f && fProportion >= 0.0f;
864 fProportion = value;
866 bool newEnabled = fPrivateData->fEnabled && fMin < fMax
867 && fProportion < 1.0f && fProportion >= 0.0f;
869 _UpdateThumbFrame();
871 if (oldEnabled != newEnabled)
872 Invalidate();
876 float
877 BScrollBar::Proportion() const
879 return fProportion;
883 void
884 BScrollBar::SetRange(float min, float max)
886 if (min > max || isnanf(min) || isnanf(max)
887 || isinff(min) || isinff(max)) {
888 min = 0.0f;
889 max = 0.0f;
892 min = roundf(min);
893 max = roundf(max);
895 if (fMin == min && fMax == max)
896 return;
898 TRACE("BScrollBar(%s)::SetRange(min=%.1f, max=%.1f)\n", Name(), min, max);
900 fMin = min;
901 fMax = max;
903 if (fValue < fMin || fValue > fMax)
904 SetValue(fValue);
905 else {
906 _UpdateThumbFrame();
907 Invalidate();
912 void
913 BScrollBar::GetRange(float* min, float* max) const
915 if (min != NULL)
916 *min = fMin;
918 if (max != NULL)
919 *max = fMax;
923 void
924 BScrollBar::SetSteps(float smallStep, float largeStep)
926 // Under R5, steps can be set only after being attached to a window,
927 // probably because the data is kept server-side. We'll just remove
928 // that limitation... :P
930 // The BeBook also says that we need to specify an integer value even
931 // though the step values are floats. For the moment, we'll just make
932 // sure that they are integers
933 smallStep = roundf(smallStep);
934 largeStep = roundf(largeStep);
935 if (fSmallStep == smallStep && fLargeStep == largeStep)
936 return;
938 TRACE("BScrollBar(%s)::SetSteps(small=%.1f, large=%.1f)\n", Name(),
939 smallStep, largeStep);
941 fSmallStep = smallStep;
942 fLargeStep = largeStep;
944 if (fProportion == 0.0) {
945 // special case, proportion is based on fLargeStep if it was never
946 // set, so it means we need to invalidate here
947 _UpdateThumbFrame();
948 Invalidate();
951 // TODO: test use of fractional values and make them work properly if
952 // they don't
956 void
957 BScrollBar::GetSteps(float* smallStep, float* largeStep) const
959 if (smallStep != NULL)
960 *smallStep = fSmallStep;
962 if (largeStep != NULL)
963 *largeStep = fLargeStep;
967 void
968 BScrollBar::SetTarget(BView* target)
970 if (fTarget) {
971 // unset the previous target's scrollbar pointer
972 if (fOrientation == B_VERTICAL)
973 fTarget->fVerScroller = NULL;
974 else
975 fTarget->fHorScroller = NULL;
978 fTarget = target;
979 if (fTarget) {
980 if (fOrientation == B_VERTICAL)
981 fTarget->fVerScroller = this;
982 else
983 fTarget->fHorScroller = this;
988 void
989 BScrollBar::SetTarget(const char* targetName)
991 // NOTE 1: BeOS implementation crashes for targetName == NULL
992 // NOTE 2: BeOS implementation also does not modify the target
993 // if it can't be found
994 if (targetName == NULL)
995 return;
997 if (Window() == NULL)
998 debugger("Method requires window and doesn't have one");
1000 BView* target = Window()->FindView(targetName);
1001 if (target != NULL)
1002 SetTarget(target);
1006 BView*
1007 BScrollBar::Target() const
1009 return fTarget;
1013 void
1014 BScrollBar::SetOrientation(orientation direction)
1016 if (fOrientation == direction)
1017 return;
1019 fOrientation = direction;
1020 InvalidateLayout();
1021 Invalidate();
1025 orientation
1026 BScrollBar::Orientation() const
1028 return fOrientation;
1032 status_t
1033 BScrollBar::SetBorderHighlighted(bool highlight)
1035 if (fPrivateData->fBorderHighlighted == highlight)
1036 return B_OK;
1038 fPrivateData->fBorderHighlighted = highlight;
1040 BRect dirty(Bounds());
1041 if (fOrientation == B_HORIZONTAL)
1042 dirty.bottom = dirty.top;
1043 else
1044 dirty.right = dirty.left;
1046 Invalidate(dirty);
1048 return B_OK;
1052 void
1053 BScrollBar::GetPreferredSize(float* _width, float* _height)
1055 if (fOrientation == B_VERTICAL) {
1056 if (_width)
1057 *_width = B_V_SCROLL_BAR_WIDTH;
1059 if (_height)
1060 *_height = Bounds().Height();
1061 } else if (fOrientation == B_HORIZONTAL) {
1062 if (_width)
1063 *_width = Bounds().Width();
1065 if (_height)
1066 *_height = B_H_SCROLL_BAR_HEIGHT;
1071 void
1072 BScrollBar::ResizeToPreferred()
1074 BView::ResizeToPreferred();
1079 void
1080 BScrollBar::MakeFocus(bool focus)
1082 BView::MakeFocus(focus);
1086 BSize
1087 BScrollBar::MinSize()
1089 return BLayoutUtils::ComposeSize(ExplicitMinSize(), _MinSize());
1093 BSize
1094 BScrollBar::MaxSize()
1096 BSize maxSize = _MinSize();
1097 if (fOrientation == B_HORIZONTAL)
1098 maxSize.width = B_SIZE_UNLIMITED;
1099 else
1100 maxSize.height = B_SIZE_UNLIMITED;
1101 return BLayoutUtils::ComposeSize(ExplicitMaxSize(), maxSize);
1105 BSize
1106 BScrollBar::PreferredSize()
1108 BSize preferredSize = _MinSize();
1109 if (fOrientation == B_HORIZONTAL)
1110 preferredSize.width *= 2;
1111 else
1112 preferredSize.height *= 2;
1114 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), preferredSize);
1118 status_t
1119 BScrollBar::GetSupportedSuites(BMessage* message)
1121 return BView::GetSupportedSuites(message);
1125 BHandler*
1126 BScrollBar::ResolveSpecifier(BMessage* message, int32 index,
1127 BMessage* specifier, int32 what, const char* property)
1129 return BView::ResolveSpecifier(message, index, specifier, what, property);
1133 status_t
1134 BScrollBar::Perform(perform_code code, void* _data)
1136 switch (code) {
1137 case PERFORM_CODE_MIN_SIZE:
1138 ((perform_data_min_size*)_data)->return_value
1139 = BScrollBar::MinSize();
1141 return B_OK;
1143 case PERFORM_CODE_MAX_SIZE:
1144 ((perform_data_max_size*)_data)->return_value
1145 = BScrollBar::MaxSize();
1147 return B_OK;
1149 case PERFORM_CODE_PREFERRED_SIZE:
1150 ((perform_data_preferred_size*)_data)->return_value
1151 = BScrollBar::PreferredSize();
1153 return B_OK;
1155 case PERFORM_CODE_LAYOUT_ALIGNMENT:
1156 ((perform_data_layout_alignment*)_data)->return_value
1157 = BScrollBar::LayoutAlignment();
1159 return B_OK;
1161 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
1162 ((perform_data_has_height_for_width*)_data)->return_value
1163 = BScrollBar::HasHeightForWidth();
1165 return B_OK;
1167 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
1169 perform_data_get_height_for_width* data
1170 = (perform_data_get_height_for_width*)_data;
1171 BScrollBar::GetHeightForWidth(data->width, &data->min, &data->max,
1172 &data->preferred);
1174 return B_OK;
1177 case PERFORM_CODE_SET_LAYOUT:
1179 perform_data_set_layout* data = (perform_data_set_layout*)_data;
1180 BScrollBar::SetLayout(data->layout);
1182 return B_OK;
1185 case PERFORM_CODE_LAYOUT_INVALIDATED:
1187 perform_data_layout_invalidated* data
1188 = (perform_data_layout_invalidated*)_data;
1189 BScrollBar::LayoutInvalidated(data->descendants);
1191 return B_OK;
1194 case PERFORM_CODE_DO_LAYOUT:
1196 BScrollBar::DoLayout();
1198 return B_OK;
1202 return BView::Perform(code, _data);
1206 void BScrollBar::_ReservedScrollBar1() {}
1207 void BScrollBar::_ReservedScrollBar2() {}
1208 void BScrollBar::_ReservedScrollBar3() {}
1209 void BScrollBar::_ReservedScrollBar4() {}
1212 BScrollBar&
1213 BScrollBar::operator=(const BScrollBar&)
1215 return *this;
1219 bool
1220 BScrollBar::_DoubleArrows() const
1222 if (!fPrivateData->fScrollBarInfo.double_arrows)
1223 return false;
1225 // if there is not enough room, switch to single arrows even though
1226 // double arrows is specified
1227 if (fOrientation == B_HORIZONTAL) {
1228 return Bounds().Width() > (Bounds().Height() + 1) * 4
1229 + fPrivateData->fScrollBarInfo.min_knob_size * 2;
1230 } else {
1231 return Bounds().Height() > (Bounds().Width() + 1) * 4
1232 + fPrivateData->fScrollBarInfo.min_knob_size * 2;
1237 void
1238 BScrollBar::_UpdateThumbFrame()
1240 BRect bounds = Bounds();
1241 bounds.InsetBy(1.0, 1.0);
1243 BRect oldFrame = fPrivateData->fThumbFrame;
1244 fPrivateData->fThumbFrame = bounds;
1245 float minSize = fPrivateData->fScrollBarInfo.min_knob_size;
1246 float maxSize;
1247 float buttonSize;
1249 // assume square buttons
1250 if (fOrientation == B_VERTICAL) {
1251 maxSize = bounds.Height();
1252 buttonSize = bounds.Width() + 1.0;
1253 } else {
1254 maxSize = bounds.Width();
1255 buttonSize = bounds.Height() + 1.0;
1258 if (_DoubleArrows()) {
1259 // subtract the size of four buttons
1260 maxSize -= buttonSize * 4;
1261 } else {
1262 // subtract the size of two buttons
1263 maxSize -= buttonSize * 2;
1265 // visual adjustments (room for darker line between thumb and buttons)
1266 maxSize--;
1268 float thumbSize;
1269 if (fPrivateData->fScrollBarInfo.proportional) {
1270 float proportion = fProportion;
1271 if (fMin >= fMax || proportion > 1.0 || proportion < 0.0)
1272 proportion = 1.0;
1274 if (proportion == 0.0) {
1275 // Special case a proportion of 0.0, use the large step value
1276 // in that case (NOTE: fMin == fMax already handled above)
1277 // This calculation is based on the assumption that "large step"
1278 // scrolls by one "page size".
1279 proportion = fLargeStep / (2 * (fMax - fMin));
1280 if (proportion > 1.0)
1281 proportion = 1.0;
1283 thumbSize = maxSize * proportion;
1284 if (thumbSize < minSize)
1285 thumbSize = minSize;
1286 } else
1287 thumbSize = minSize;
1289 thumbSize = floorf(thumbSize + 0.5);
1290 thumbSize--;
1292 // the thumb can be scrolled within the remaining area "maxSize - thumbSize - 1.0"
1293 float offset = 0.0;
1294 if (fMax > fMin) {
1295 offset = floorf(((fValue - fMin) / (fMax - fMin))
1296 * (maxSize - thumbSize - 1.0));
1299 if (_DoubleArrows()) {
1300 offset += buttonSize * 2;
1301 } else
1302 offset += buttonSize;
1304 // visual adjustments (room for darker line between thumb and buttons)
1305 offset++;
1307 if (fOrientation == B_VERTICAL) {
1308 fPrivateData->fThumbFrame.bottom = fPrivateData->fThumbFrame.top
1309 + thumbSize;
1310 fPrivateData->fThumbFrame.OffsetBy(0.0, offset);
1311 } else {
1312 fPrivateData->fThumbFrame.right = fPrivateData->fThumbFrame.left
1313 + thumbSize;
1314 fPrivateData->fThumbFrame.OffsetBy(offset, 0.0);
1317 if (Window() != NULL) {
1318 BRect invalid = oldFrame.IsValid()
1319 ? oldFrame | fPrivateData->fThumbFrame
1320 : fPrivateData->fThumbFrame;
1321 // account for those two dark lines
1322 if (fOrientation == B_HORIZONTAL)
1323 invalid.InsetBy(-2.0, 0.0);
1324 else
1325 invalid.InsetBy(0.0, -2.0);
1327 Invalidate(invalid);
1332 float
1333 BScrollBar::_ValueFor(BPoint where) const
1335 BRect bounds = Bounds();
1336 bounds.InsetBy(1.0f, 1.0f);
1338 float offset;
1339 float thumbSize;
1340 float maxSize;
1341 float buttonSize;
1343 if (fOrientation == B_VERTICAL) {
1344 offset = where.y;
1345 thumbSize = fPrivateData->fThumbFrame.Height();
1346 maxSize = bounds.Height();
1347 buttonSize = bounds.Width() + 1.0f;
1348 } else {
1349 offset = where.x;
1350 thumbSize = fPrivateData->fThumbFrame.Width();
1351 maxSize = bounds.Width();
1352 buttonSize = bounds.Height() + 1.0f;
1355 if (_DoubleArrows()) {
1356 // subtract the size of four buttons
1357 maxSize -= buttonSize * 4;
1358 // convert point to inside of area between buttons
1359 offset -= buttonSize * 2;
1360 } else {
1361 // subtract the size of two buttons
1362 maxSize -= buttonSize * 2;
1363 // convert point to inside of area between buttons
1364 offset -= buttonSize;
1366 // visual adjustments (room for darker line between thumb and buttons)
1367 maxSize--;
1368 offset++;
1370 return roundf(fMin + (offset / (maxSize - thumbSize)
1371 * (fMax - fMin + 1.0f)));
1375 int32
1376 BScrollBar::_ButtonFor(BPoint where) const
1378 BRect bounds = Bounds();
1379 bounds.InsetBy(1.0f, 1.0f);
1381 float buttonSize = fOrientation == B_VERTICAL
1382 ? bounds.Width() + 1.0f
1383 : bounds.Height() + 1.0f;
1385 BRect rect(bounds.left, bounds.top,
1386 bounds.left + buttonSize, bounds.top + buttonSize);
1388 if (fOrientation == B_VERTICAL) {
1389 if (rect.Contains(where))
1390 return ARROW1;
1392 if (_DoubleArrows()) {
1393 rect.OffsetBy(0.0, buttonSize);
1394 if (rect.Contains(where))
1395 return ARROW2;
1397 rect.OffsetTo(bounds.left, bounds.bottom - 2 * buttonSize);
1398 if (rect.Contains(where))
1399 return ARROW3;
1401 rect.OffsetTo(bounds.left, bounds.bottom - buttonSize);
1402 if (rect.Contains(where))
1403 return ARROW4;
1404 } else {
1405 if (rect.Contains(where))
1406 return ARROW1;
1408 if (_DoubleArrows()) {
1409 rect.OffsetBy(buttonSize, 0.0);
1410 if (rect.Contains(where))
1411 return ARROW2;
1413 rect.OffsetTo(bounds.right - 2 * buttonSize, bounds.top);
1414 if (rect.Contains(where))
1415 return ARROW3;
1417 rect.OffsetTo(bounds.right - buttonSize, bounds.top);
1418 if (rect.Contains(where))
1419 return ARROW4;
1422 return NOARROW;
1426 BRect
1427 BScrollBar::_ButtonRectFor(int32 button) const
1429 BRect bounds = Bounds();
1430 bounds.InsetBy(1.0f, 1.0f);
1432 float buttonSize = fOrientation == B_VERTICAL
1433 ? bounds.Width() + 1.0f
1434 : bounds.Height() + 1.0f;
1436 BRect rect(bounds.left, bounds.top,
1437 bounds.left + buttonSize - 1.0f, bounds.top + buttonSize - 1.0f);
1439 if (fOrientation == B_VERTICAL) {
1440 switch (button) {
1441 case ARROW1:
1442 break;
1444 case ARROW2:
1445 rect.OffsetBy(0.0, buttonSize);
1446 break;
1448 case ARROW3:
1449 rect.OffsetTo(bounds.left, bounds.bottom - 2 * buttonSize + 1);
1450 break;
1452 case ARROW4:
1453 rect.OffsetTo(bounds.left, bounds.bottom - buttonSize + 1);
1454 break;
1456 } else {
1457 switch (button) {
1458 case ARROW1:
1459 break;
1461 case ARROW2:
1462 rect.OffsetBy(buttonSize, 0.0);
1463 break;
1465 case ARROW3:
1466 rect.OffsetTo(bounds.right - 2 * buttonSize + 1, bounds.top);
1467 break;
1469 case ARROW4:
1470 rect.OffsetTo(bounds.right - buttonSize + 1, bounds.top);
1471 break;
1475 return rect;
1479 void
1480 BScrollBar::_UpdateTargetValue(BPoint where)
1482 if (fOrientation == B_VERTICAL) {
1483 fPrivateData->fStopValue = _ValueFor(BPoint(where.x, where.y
1484 - fPrivateData->fThumbFrame.Height() / 2.0));
1485 } else {
1486 fPrivateData->fStopValue = _ValueFor(BPoint(where.x
1487 - fPrivateData->fThumbFrame.Width() / 2.0, where.y));
1492 void
1493 BScrollBar::_UpdateArrowButtons()
1495 bool upEnabled = fValue > fMin;
1496 if (fPrivateData->fUpArrowsEnabled != upEnabled) {
1497 fPrivateData->fUpArrowsEnabled = upEnabled;
1498 Invalidate(_ButtonRectFor(ARROW1));
1499 if (_DoubleArrows())
1500 Invalidate(_ButtonRectFor(ARROW3));
1503 bool downEnabled = fValue < fMax;
1504 if (fPrivateData->fDownArrowsEnabled != downEnabled) {
1505 fPrivateData->fDownArrowsEnabled = downEnabled;
1506 Invalidate(_ButtonRectFor(ARROW4));
1507 if (_DoubleArrows())
1508 Invalidate(_ButtonRectFor(ARROW2));
1513 status_t
1514 control_scrollbar(scroll_bar_info* info, BScrollBar* bar)
1516 if (bar == NULL || info == NULL)
1517 return B_BAD_VALUE;
1519 if (bar->fPrivateData->fScrollBarInfo.double_arrows
1520 != info->double_arrows) {
1521 bar->fPrivateData->fScrollBarInfo.double_arrows = info->double_arrows;
1523 int8 multiplier = (info->double_arrows) ? 1 : -1;
1525 if (bar->fOrientation == B_VERTICAL) {
1526 bar->fPrivateData->fThumbFrame.OffsetBy(0, multiplier
1527 * B_H_SCROLL_BAR_HEIGHT);
1528 } else {
1529 bar->fPrivateData->fThumbFrame.OffsetBy(multiplier
1530 * B_V_SCROLL_BAR_WIDTH, 0);
1534 bar->fPrivateData->fScrollBarInfo.proportional = info->proportional;
1536 // TODO: Figure out how proportional relates to the size of the thumb
1538 // TODO: Add redraw code to reflect the changes
1540 if (info->knob >= 0 && info->knob <= 2)
1541 bar->fPrivateData->fScrollBarInfo.knob = info->knob;
1542 else
1543 return B_BAD_VALUE;
1545 if (info->min_knob_size >= SCROLL_BAR_MINIMUM_KNOB_SIZE
1546 && info->min_knob_size <= SCROLL_BAR_MAXIMUM_KNOB_SIZE) {
1547 bar->fPrivateData->fScrollBarInfo.min_knob_size = info->min_knob_size;
1548 } else
1549 return B_BAD_VALUE;
1551 return B_OK;
1555 void
1556 BScrollBar::_DrawDisabledBackground(BRect area, const rgb_color& light,
1557 const rgb_color& dark, const rgb_color& fill)
1559 if (!area.IsValid())
1560 return;
1562 if (fOrientation == B_VERTICAL) {
1563 int32 height = area.IntegerHeight();
1564 if (height == 0) {
1565 SetHighColor(dark);
1566 StrokeLine(area.LeftTop(), area.RightTop());
1567 } else if (height == 1) {
1568 SetHighColor(dark);
1569 FillRect(area);
1570 } else {
1571 BeginLineArray(4);
1572 AddLine(BPoint(area.left, area.top),
1573 BPoint(area.right, area.top), dark);
1574 AddLine(BPoint(area.left, area.bottom - 1),
1575 BPoint(area.left, area.top + 1), light);
1576 AddLine(BPoint(area.left + 1, area.top + 1),
1577 BPoint(area.right, area.top + 1), light);
1578 AddLine(BPoint(area.right, area.bottom),
1579 BPoint(area.left, area.bottom), dark);
1580 EndLineArray();
1581 area.left++;
1582 area.top += 2;
1583 area.bottom--;
1584 if (area.IsValid()) {
1585 SetHighColor(fill);
1586 FillRect(area);
1589 } else {
1590 int32 width = area.IntegerWidth();
1591 if (width == 0) {
1592 SetHighColor(dark);
1593 StrokeLine(area.LeftBottom(), area.LeftTop());
1594 } else if (width == 1) {
1595 SetHighColor(dark);
1596 FillRect(area);
1597 } else {
1598 BeginLineArray(4);
1599 AddLine(BPoint(area.left, area.bottom),
1600 BPoint(area.left, area.top), dark);
1601 AddLine(BPoint(area.left + 1, area.bottom),
1602 BPoint(area.left + 1, area.top + 1), light);
1603 AddLine(BPoint(area.left + 1, area.top),
1604 BPoint(area.right - 1, area.top), light);
1605 AddLine(BPoint(area.right, area.top),
1606 BPoint(area.right, area.bottom), dark);
1607 EndLineArray();
1608 area.left += 2;
1609 area.top ++;
1610 area.right--;
1611 if (area.IsValid()) {
1612 SetHighColor(fill);
1613 FillRect(area);
1620 void
1621 BScrollBar::_DrawArrowButton(int32 direction, bool doubleArrows, BRect rect,
1622 const BRect& updateRect, bool enabled, bool down)
1624 if (!updateRect.Intersects(rect))
1625 return;
1627 uint32 flags = 0;
1628 if (!enabled)
1629 flags |= BControlLook::B_DISABLED;
1631 if (down && fPrivateData->fDoRepeat)
1632 flags |= BControlLook::B_ACTIVATED;
1634 // TODO: Why does BControlLook need this as the base color for the
1635 // scrollbar to look right?
1636 rgb_color baseColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
1637 B_LIGHTEN_1_TINT);
1639 be_control_look->DrawButtonBackground(this, rect, updateRect, baseColor,
1640 flags, BControlLook::B_ALL_BORDERS, fOrientation);
1642 // TODO: Why does BControlLook need this negative inset for the arrow to
1643 // look right?
1644 rect.InsetBy(-1.0f, -1.0f);
1645 be_control_look->DrawArrowShape(this, rect, updateRect,
1646 baseColor, direction, flags, B_DARKEN_MAX_TINT);
1650 BSize
1651 BScrollBar::_MinSize() const
1653 BSize minSize;
1654 if (fOrientation == B_HORIZONTAL) {
1655 minSize.width = 2 * B_V_SCROLL_BAR_WIDTH
1656 + 2 * fPrivateData->fScrollBarInfo.min_knob_size;
1657 minSize.height = B_H_SCROLL_BAR_HEIGHT;
1658 } else {
1659 minSize.width = B_V_SCROLL_BAR_WIDTH;
1660 minSize.height = 2 * B_H_SCROLL_BAR_HEIGHT
1661 + 2 * fPrivateData->fScrollBarInfo.min_knob_size;
1664 return minSize;