xref: /haiku/src/preferences/appearance/FakeScrollBar.cpp (revision c4a2fb4bd742aeba56ad882ecddd18b42d94668b)
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 knobWidth = 0;
152 	int32 knobHeight = 0;
153 
154 	if (square) {
155 		knobWidth = 2;
156 		knobHeight = 2;
157 	} else {
158 		knobWidth = 1;
159 		knobHeight = 3;
160 	}
161 
162 	float hmiddle = bgRect.Width() / 2;
163 	float vmiddle = bgRect.Height() / 2;
164 
165 	BRect middleKnob = BRect(bgRect.left + hmiddle - knobWidth,
166 		bgRect.top + vmiddle - knobHeight,
167 		bgRect.left + hmiddle + knobWidth,
168 		bgRect.top + vmiddle + knobHeight);
169 
170 	BRect leftKnob = middleKnob.OffsetByCopy(knobWidth * -4, 0);
171 	be_control_look->DrawButtonBackground(this, leftKnob, updateRect,
172 		normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL);
173 
174 	BRect rightKnob = middleKnob.OffsetByCopy(knobWidth * 4, 0);
175 	be_control_look->DrawButtonBackground(this, rightKnob, updateRect,
176 		normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL);
177 
178 	// draw middle knob last because it modifies middleKnob
179 	be_control_look->DrawButtonBackground(this, middleKnob, updateRect,
180 		normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL);
181 }
182 
183 
184 void
185 FakeScrollBar::MouseDown(BPoint point)
186 {
187 	BControl::MouseDown(point);
188 }
189 
190 
191 void
192 FakeScrollBar::MouseMoved(BPoint point, uint32 transit,
193 	const BMessage* message)
194 {
195 	BControl::MouseMoved(point, transit, message);
196 }
197 
198 
199 void
200 FakeScrollBar::MouseUp(BPoint point)
201 {
202 	SetValue(B_CONTROL_ON);
203 	Invoke();
204 
205 	Invalidate();
206 
207 	BControl::MouseUp(point);
208 }
209 
210 
211 void
212 FakeScrollBar::SetValue(int32 value)
213 {
214 	if (value != Value()) {
215 		BControl::SetValueNoUpdate(value);
216 		Invalidate();
217 	}
218 
219 	if (!value)
220 		return;
221 
222 	BView* parent = Parent();
223 	BView* child = NULL;
224 
225 	if (parent != NULL) {
226 		// If the parent is a BBox, the group parent is the parent of the BBox
227 		BBox* box = dynamic_cast<BBox*>(parent);
228 
229 		if (box && box->LabelView() == this)
230 			parent = box->Parent();
231 
232 		if (parent != NULL) {
233 			BBox* box = dynamic_cast<BBox*>(parent);
234 
235 			// If the parent is a BBox, skip the label if there is one
236 			if (box && box->LabelView())
237 				child = parent->ChildAt(1);
238 			else
239 				child = parent->ChildAt(0);
240 		} else
241 			child = Window()->ChildAt(0);
242 	} else if (Window())
243 		child = Window()->ChildAt(0);
244 
245 	while (child) {
246 		FakeScrollBar* scrollbar = dynamic_cast<FakeScrollBar*>(child);
247 
248 		if (scrollbar != NULL && (scrollbar != this))
249 			scrollbar->SetValue(B_CONTROL_OFF);
250 		else {
251 			// If the child is a BBox, check if the label is a scrollbarbutton
252 			BBox* box = dynamic_cast<BBox*>(child);
253 
254 			if (box && box->LabelView()) {
255 				scrollbar = dynamic_cast<FakeScrollBar*>(box->LabelView());
256 
257 				if (scrollbar != NULL && (scrollbar != this))
258 					scrollbar->SetValue(B_CONTROL_OFF);
259 			}
260 		}
261 
262 		child = child->NextSibling();
263 	}
264 
265 	//ASSERT(Value() == B_CONTROL_ON);
266 }
267 
268 
269 //	#pragma mark -
270 
271 
272 void
273 FakeScrollBar::SetDoubleArrows(bool doubleArrows)
274 {
275 	fDoubleArrows = doubleArrows;
276 	Invalidate();
277 }
278 
279 
280 void
281 FakeScrollBar::SetKnobStyle(uint32 knobStyle)
282 {
283 	fKnobStyle = knobStyle;
284 	Invalidate();
285 }
286 
287 
288 void
289 FakeScrollBar::SetFromScrollBarInfo(const scroll_bar_info &info)
290 {
291 	fDoubleArrows = info.double_arrows;
292 	fKnobStyle = info.knob;
293 	Invalidate();
294 }
295 
296 
297 //	#pragma mark -
298 
299 
300 void
301 FakeScrollBar::_DrawArrowButton(int32 direction, bool doubleArrows, BRect r,
302 	const BRect& updateRect)
303 {
304 	if (!updateRect.Intersects(r))
305 		return;
306 
307 	rgb_color c = ui_color(B_PANEL_BACKGROUND_COLOR);
308 	rgb_color light = tint_color(c, B_LIGHTEN_MAX_TINT);
309 	rgb_color dark = tint_color(c, B_DARKEN_1_TINT);
310 	rgb_color darker = tint_color(c, B_DARKEN_2_TINT);
311 	rgb_color normal = c;
312 	rgb_color arrow = tint_color(c,
313 		(B_DARKEN_MAX_TINT + B_DARKEN_4_TINT) / 2.0);
314 
315 	BPoint tri1, tri2, tri3;
316 	float hInset = r.Width() / 3;
317 	float vInset = r.Height() / 3;
318 	r.InsetBy(hInset, vInset);
319 
320 	switch (direction) {
321 		case ARROW_LEFT:
322 			tri1.Set(r.right, r.top);
323 			tri2.Set(r.right - r.Width() / 1.33, (r.top + r.bottom + 1) / 2);
324 			tri3.Set(r.right, r.bottom + 1);
325 			break;
326 
327 		case ARROW_RIGHT:
328 			tri1.Set(r.left, r.bottom + 1);
329 			tri2.Set(r.left + r.Width() / 1.33, (r.top + r.bottom + 1) / 2);
330 			tri3.Set(r.left, r.top);
331 			break;
332 
333 		case ARROW_UP:
334 			tri1.Set(r.left, r.bottom);
335 			tri2.Set((r.left + r.right + 1) / 2, r.bottom - r.Height() / 1.33);
336 			tri3.Set(r.right + 1, r.bottom);
337 			break;
338 
339 		default:
340 			tri1.Set(r.left, r.top);
341 			tri2.Set((r.left + r.right + 1) / 2, r.top + r.Height() / 1.33);
342 			tri3.Set(r.right + 1, r.top);
343 			break;
344 	}
345 
346 	r.InsetBy(-(hInset - 1), -(vInset - 1));
347 	BRect temp(r.InsetByCopy(-1, -1));
348 	be_control_look->DrawButtonBackground(this, temp, updateRect,
349 		normal, 0, BControlLook::B_ALL_BORDERS, B_HORIZONTAL);
350 
351 	BShape arrowShape;
352 	arrowShape.MoveTo(tri1);
353 	arrowShape.LineTo(tri2);
354 	arrowShape.LineTo(tri3);
355 
356 	SetHighColor(arrow);
357 	SetPenSize(ceilf(hInset / 2.0));
358 	StrokeShape(&arrowShape);
359 	SetPenSize(1.0);
360 }
361