xref: /haiku/src/preferences/appearance/FakeScrollBar.cpp (revision dfa8cf8c05bd7173fa5f25170bc297757175daab)
1 /*
2  *  Copyright 2010-2012 Haiku, Inc. All rights reserved.
3  *  Distributed under the terms of the MIT license.
4  *
5  *	Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  *		John Scipione <jscipione@gmail.com>
8  */
9 
10 
11 #include "FakeScrollBar.h"
12 
13 #include <Box.h>
14 #include <ControlLook.h>
15 #include <Message.h>
16 #include <ScrollBar.h>
17 #include <Shape.h>
18 #include <Size.h>
19 #include <Window.h>
20 
21 
22 typedef enum {
23 	ARROW_LEFT = 0,
24 	ARROW_RIGHT,
25 	ARROW_UP,
26 	ARROW_DOWN,
27 	ARROW_NONE
28 } arrow_direction;
29 
30 
31 FakeScrollBar::FakeScrollBar(bool drawArrows, bool doubleArrows,
32 	int32 knobStyle, BMessage* message)
33 	:
34 	BControl("FakeScrollBar", NULL, message, B_WILL_DRAW | B_NAVIGABLE),
35 	fDrawArrows(drawArrows),
36 	fDoubleArrows(doubleArrows),
37 	fKnobStyle(knobStyle)
38 {
39 	SetExplicitMinSize(BSize(160, 20));
40 	SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 20));
41 }
42 
43 
44 FakeScrollBar::~FakeScrollBar(void)
45 {
46 }
47 
48 
49 void
50 FakeScrollBar::Draw(BRect updateRect)
51 {
52 	BRect bounds = Bounds();
53 
54 	rgb_color normal = ui_color(B_PANEL_BACKGROUND_COLOR);
55 
56 	if (IsFocus()) {
57 		// draw the focus indicator
58 		SetHighColor(ui_color(B_NAVIGATION_BASE_COLOR));
59 		StrokeRect(bounds);
60 		bounds.InsetBy(1.0, 1.0);
61 
62 		// Draw the selected border (1px)
63 		if (Value() == B_CONTROL_ON)
64 			SetHighColor(ui_color(B_CONTROL_MARK_COLOR));
65 		else
66 			SetHighColor(normal);
67 
68 		StrokeRect(bounds);
69 		bounds.InsetBy(1.0, 1.0);
70 	} else {
71 		// Draw the selected border (2px)
72 		if (Value() == B_CONTROL_ON)
73 			SetHighColor(ui_color(B_CONTROL_MARK_COLOR));
74 		else
75 			SetHighColor(normal);
76 
77 		StrokeRect(bounds);
78 		bounds.InsetBy(1.0, 1.0);
79 		StrokeRect(bounds);
80 		bounds.InsetBy(1.0, 1.0);
81 	}
82 
83 	// draw a gap (1px)
84 	SetHighColor(normal);
85 	StrokeRect(bounds);
86 	bounds.InsetBy(1.0, 1.0);
87 
88 	// draw a border around control (1px)
89 	SetHighColor(tint_color(normal, B_DARKEN_1_TINT));
90 	StrokeRect(bounds);
91 	bounds.InsetBy(1.0, 1.0);
92 
93 	BRect thumbBG = bounds;
94 	BRect bgRect = bounds;
95 
96 	if (fDrawArrows) {
97 		// draw arrows
98 		SetDrawingMode(B_OP_OVER);
99 
100 		BRect buttonFrame(bounds.left, bounds.top,
101 			bounds.left + bounds.Height(), bounds.bottom);
102 
103 		_DrawArrowButton(ARROW_LEFT, fDoubleArrows, buttonFrame, updateRect);
104 
105 		if (fDoubleArrows) {
106 			buttonFrame.OffsetBy(bounds.Height() + 1, 0.0);
107 			_DrawArrowButton(ARROW_RIGHT, fDoubleArrows, buttonFrame,
108 				updateRect);
109 
110 			buttonFrame.OffsetTo(bounds.right - ((bounds.Height() * 2) + 1),
111 				bounds.top);
112 			_DrawArrowButton(ARROW_LEFT, fDoubleArrows, buttonFrame,
113 				updateRect);
114 
115 			thumbBG.left += bounds.Height() * 2 + 2;
116 			thumbBG.right -= bounds.Height() * 2 + 2;
117 		} else {
118 			thumbBG.left += bounds.Height() + 1;
119 			thumbBG.right -= bounds.Height() + 1;
120 		}
121 
122 		buttonFrame.OffsetTo(bounds.right - bounds.Height(), bounds.top);
123 		_DrawArrowButton(ARROW_RIGHT, fDoubleArrows, buttonFrame, updateRect);
124 
125 		SetDrawingMode(B_OP_COPY);
126 
127 		bgRect = bounds.InsetByCopy(48, 0);
128 	} else
129 		bgRect = bounds.InsetByCopy(16, 0);
130 
131 	// fill background besides the thumb
132 	BRect leftOfThumb(thumbBG.left, thumbBG.top, bgRect.left - 1,
133 		thumbBG.bottom);
134 	BRect rightOfThumb(bgRect.right + 1, thumbBG.top, thumbBG.right,
135 		thumbBG.bottom);
136 
137 	be_control_look->DrawScrollBarBackground(this, leftOfThumb,
138 		rightOfThumb, updateRect, normal, 0, B_HORIZONTAL);
139 
140 	// Draw scroll thumb
141 
142 	// fill the clickable surface of the thumb
143 	be_control_look->DrawButtonBackground(this, bgRect, updateRect,
144 		normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL);
145 
146 	if (fKnobStyle == B_KNOB_STYLE_NONE)
147 		return;
148 
149 	// draw the scrollbar thumb knobs
150 	bool square = fKnobStyle == B_KNOB_STYLE_DOTS;
151 	int32 hextent = 0;
152 	int32 vextent = 0;
153 
154 	if (square) {
155 		hextent = 2;
156 		vextent = 2;
157 	} else {
158 		hextent = 1;
159 		vextent = 3;
160 	}
161 
162 	float hmiddle = bgRect.Width() / 2;
163 	float vmiddle = bgRect.Height() / 2;
164 
165 	BRect middleKnob = BRect(bgRect.left + hmiddle - hextent,
166 		bgRect.top + vmiddle - vextent,
167 		bgRect.left + hmiddle + hextent,
168 		bgRect.top + vmiddle + vextent);
169 
170 	BRect leftKnob = middleKnob.OffsetByCopy(hextent * -4, 0);
171 	if (leftKnob.left > bgRect.left + hextent) {
172 		be_control_look->DrawButtonBackground(this, leftKnob, updateRect,
173 			normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL);
174 	}
175 
176 	BRect rightKnob = middleKnob.OffsetByCopy(hextent * 4, 0);
177 	if (rightKnob.right < bgRect.right - hextent) {
178 		be_control_look->DrawButtonBackground(this, rightKnob, updateRect,
179 			normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL);
180 	}
181 
182 	// draw middle knob last because it modifies middleKnob
183 	be_control_look->DrawButtonBackground(this, middleKnob, updateRect,
184 		normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL);
185 }
186 
187 
188 void
189 FakeScrollBar::MouseDown(BPoint point)
190 {
191 	BControl::MouseDown(point);
192 }
193 
194 
195 void
196 FakeScrollBar::MouseMoved(BPoint point, uint32 transit,
197 	const BMessage* message)
198 {
199 	BControl::MouseMoved(point, transit, message);
200 }
201 
202 
203 void
204 FakeScrollBar::MouseUp(BPoint point)
205 {
206 	SetValue(B_CONTROL_ON);
207 	Invoke();
208 
209 	Invalidate();
210 
211 	BControl::MouseUp(point);
212 }
213 
214 
215 void
216 FakeScrollBar::SetValue(int32 value)
217 {
218 	if (value != Value()) {
219 		BControl::SetValueNoUpdate(value);
220 		Invalidate();
221 	}
222 
223 	if (!value)
224 		return;
225 
226 	BView* parent = Parent();
227 	BView* child = NULL;
228 
229 	if (parent != NULL) {
230 		// If the parent is a BBox, the group parent is the parent of the BBox
231 		BBox* box = dynamic_cast<BBox*>(parent);
232 
233 		if (box && box->LabelView() == this)
234 			parent = box->Parent();
235 
236 		if (parent != NULL) {
237 			BBox* box = dynamic_cast<BBox*>(parent);
238 
239 			// If the parent is a BBox, skip the label if there is one
240 			if (box && box->LabelView())
241 				child = parent->ChildAt(1);
242 			else
243 				child = parent->ChildAt(0);
244 		} else
245 			child = Window()->ChildAt(0);
246 	} else if (Window())
247 		child = Window()->ChildAt(0);
248 
249 	while (child) {
250 		FakeScrollBar* scrollbar = dynamic_cast<FakeScrollBar*>(child);
251 
252 		if (scrollbar != NULL && (scrollbar != this))
253 			scrollbar->SetValue(B_CONTROL_OFF);
254 		else {
255 			// If the child is a BBox, check if the label is a scrollbarbutton
256 			BBox* box = dynamic_cast<BBox*>(child);
257 
258 			if (box && box->LabelView()) {
259 				scrollbar = dynamic_cast<FakeScrollBar*>(box->LabelView());
260 
261 				if (scrollbar != NULL && (scrollbar != this))
262 					scrollbar->SetValue(B_CONTROL_OFF);
263 			}
264 		}
265 
266 		child = child->NextSibling();
267 	}
268 
269 	//ASSERT(Value() == B_CONTROL_ON);
270 }
271 
272 
273 //	#pragma mark -
274 
275 
276 void
277 FakeScrollBar::SetDoubleArrows(bool doublearrows)
278 {
279 	fDoubleArrows = doublearrows;
280 	Invalidate();
281 }
282 
283 
284 void
285 FakeScrollBar::SetKnobStyle(uint32 style)
286 {
287 	fKnobStyle = style;
288 	Invalidate();
289 }
290 
291 
292 void
293 FakeScrollBar::SetFromScrollBarInfo(const scroll_bar_info &info)
294 {
295 	fDoubleArrows = info.double_arrows;
296 	fKnobStyle = info.knob;
297 	Invalidate();
298 }
299 
300 
301 //	#pragma mark -
302 
303 
304 void
305 FakeScrollBar::_DrawArrowButton(int32 direction, bool doubleArrows, BRect r,
306 	const BRect& updateRect)
307 {
308 	if (!updateRect.Intersects(r))
309 		return;
310 
311 	rgb_color c = ui_color(B_PANEL_BACKGROUND_COLOR);
312 	rgb_color light = tint_color(c, B_LIGHTEN_MAX_TINT);
313 	rgb_color dark = tint_color(c, B_DARKEN_1_TINT);
314 	rgb_color darker = tint_color(c, B_DARKEN_2_TINT);
315 	rgb_color normal = c;
316 	rgb_color arrow = tint_color(c,
317 		(B_DARKEN_MAX_TINT + B_DARKEN_4_TINT) / 2.0);
318 
319 	BPoint tri1, tri2, tri3;
320 	float hInset = r.Width() / 3;
321 	float vInset = r.Height() / 3;
322 	r.InsetBy(hInset, vInset);
323 
324 	switch (direction) {
325 		case ARROW_LEFT:
326 			tri1.Set(r.right, r.top);
327 			tri2.Set(r.right - r.Width() / 1.33, (r.top + r.bottom + 1) / 2);
328 			tri3.Set(r.right, r.bottom + 1);
329 			break;
330 
331 		case ARROW_RIGHT:
332 			tri1.Set(r.left, r.bottom + 1);
333 			tri2.Set(r.left + r.Width() / 1.33, (r.top + r.bottom + 1) / 2);
334 			tri3.Set(r.left, r.top);
335 			break;
336 
337 		case ARROW_UP:
338 			tri1.Set(r.left, r.bottom);
339 			tri2.Set((r.left + r.right + 1) / 2, r.bottom - r.Height() / 1.33);
340 			tri3.Set(r.right + 1, r.bottom);
341 			break;
342 
343 		default:
344 			tri1.Set(r.left, r.top);
345 			tri2.Set((r.left + r.right + 1) / 2, r.top + r.Height() / 1.33);
346 			tri3.Set(r.right + 1, r.top);
347 			break;
348 	}
349 
350 	r.InsetBy(-(hInset - 1), -(vInset - 1));
351 	BRect temp(r.InsetByCopy(-1, -1));
352 	be_control_look->DrawButtonBackground(this, temp, updateRect,
353 		normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL);
354 
355 	BShape arrowShape;
356 	arrowShape.MoveTo(tri1);
357 	arrowShape.LineTo(tri2);
358 	arrowShape.LineTo(tri3);
359 
360 	SetHighColor(arrow);
361 	SetPenSize(ceilf(hInset / 2.0));
362 	StrokeShape(&arrowShape);
363 	SetPenSize(1.0);
364 }
365