xref: /haiku/src/apps/mediaplayer/interface/SeekSlider.cpp (revision 4f2fd49bdc6078128b1391191e4edac647044c3d)
1 /*
2  * Copyright © 2006-2008 Stephan Aßmus <superstippi@gmx.de>
3  * All rights reserved. Distributed under the terms of the MIT License.
4  */
5 
6 // NOTE: Based on my code in the BeOS interface for the VLC media player
7 // that I did during the VLC 0.4.3 - 0.4.6 times. Code not written by me
8 // removed. -Stephan Aßmus
9 
10 #include "SeekSlider.h"
11 
12 #include <stdio.h>
13 #include <string.h>
14 
15 #include "DrawingTidbits.h"
16 
17 
18 #define SEEK_SLIDER_KNOB_WIDTH 8.0
19 
20 const rgb_color kSeekGreen = (rgb_color){ 171, 221, 161, 255 };
21 const rgb_color kSeekGreenShadow = (rgb_color){ 144, 186, 136, 255 };
22 const rgb_color kSeekRed = (rgb_color){ 255, 0, 0, 255 };
23 const rgb_color kSeekRedLight = (rgb_color){ 255, 152, 152, 255 };
24 const rgb_color kSeekRedShadow = (rgb_color){ 178, 0, 0, 255 };
25 
26 const char* kDisabledSeekMessage = "Drop files to play";
27 
28 
29 SeekSlider::SeekSlider(BRect frame, const char* name, BMessage* message,
30 					   int32 minValue, int32 maxValue)
31 	: BControl(frame, name, NULL, message, B_FOLLOW_LEFT | B_FOLLOW_TOP,
32 			   B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE | B_FRAME_EVENTS)
33 	, fTracking(false)
34 	, fLastTrackTime(0)
35 	, fKnobPos(_KnobPosFor(Bounds(), Value()))
36 	, fMinValue(minValue)
37 	, fMaxValue(maxValue)
38 {
39 	BFont font(be_plain_font);
40 	font.SetSize(9.0);
41 	SetFont(&font);
42 }
43 
44 
45 SeekSlider::~SeekSlider()
46 {
47 }
48 
49 
50 void
51 SeekSlider::AttachedToWindow()
52 {
53 	BControl::AttachedToWindow();
54 	SetViewColor(B_TRANSPARENT_32_BIT);
55 }
56 
57 
58 void
59 SeekSlider::SetValue(int32 value)
60 {
61 	if (value == Value())
62 		return;
63 
64 #if __HAIKU__
65 	BControl::SetValueNoUpdate(value);
66 #else
67 	BControl::SetValue(value);
68 		// this will Invalidate() the whole view
69 #endif
70 	Invoke();
71 
72 	_SetKnobPosition(_KnobPosFor(Bounds(), Value()));
73 
74 	fLastTrackTime = system_time();
75 }
76 
77 
78 void
79 SeekSlider::Draw(BRect updateRect)
80 {
81 	BRect r(Bounds());
82 
83 	// draw both sides (the original from Be doesn't seem
84 	// to make a difference for enabled/disabled state)
85 //	DrawBitmapAsync(fLeftSideBits, r.LeftTop());
86 //	DrawBitmapAsync(fRightSideBits, BPoint(sliderEnd + 1.0, r.top));
87 	// colors for the slider area between the two bitmaps
88 	rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR);
89 	rgb_color shadow = tint_color(background, B_DARKEN_2_TINT);
90 	rgb_color softShadow = tint_color(background, B_DARKEN_1_TINT);
91 	rgb_color darkShadow = tint_color(background, B_DARKEN_4_TINT);
92 	rgb_color midShadow = tint_color(background, B_DARKEN_3_TINT);
93 	rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
94 	rgb_color softLight = tint_color(background, B_LIGHTEN_1_TINT);
95 	rgb_color green = kSeekGreen;
96 	rgb_color greenShadow = kSeekGreenShadow;
97 	rgb_color black = kBlack;
98 	rgb_color dotGrey = midShadow;
99 	rgb_color dotGreen = greenShadow;
100 	// draw frame
101 	_StrokeFrame(r, softShadow, softShadow, light, light);
102 	r.InsetBy(1.0, 1.0);
103 	_StrokeFrame(r, black, black, softShadow, softShadow);
104 	if (IsEnabled()) {
105 		// *** enabled look ***
106 		r.InsetBy(1.0, 1.0);
107 		// inner shadow
108 		_StrokeFrame(r, greenShadow, greenShadow, green, green);
109 		r.top++;
110 		r.left++;
111 		_StrokeFrame(r, greenShadow, greenShadow, green, green);
112 		// inside area
113 		r.InsetBy(1.0, 1.0);
114 		SetHighColor(green);
115 		FillRect(r);
116 		// dots
117 		int32 dotCount = (int32)(r.Width() / 6.0);
118 		BPoint dotPos;
119 		dotPos.y = r.top + 2.0;
120 		SetHighColor(dotGreen);
121 
122 		float knobWidth2 = SEEK_SLIDER_KNOB_WIDTH / 2.0;
123 		float sliderStart = (r.left + knobWidth2);
124 
125 		for (int32 i = 0; i < dotCount; i++) {
126 			dotPos.x = sliderStart + i * 6.0;
127 			StrokeLine(dotPos, BPoint(dotPos.x, dotPos.y + 6.0));
128 		}
129 		// slider handle
130 		r.top -= 4.0;
131 		r.bottom += 3.0;
132 		r.left = fKnobPos - knobWidth2;
133 		r.right = fKnobPos + knobWidth2;
134 		// black outline
135 		float handleBottomSize = 2.0;
136 		float handleArrowSize = 6.0;
137 		BeginLineArray(10);
138 			// upper handle
139 			AddLine(BPoint(r.left, r.top + handleBottomSize),
140 					BPoint(r.left, r.top), black);
141 			AddLine(BPoint(r.left + 1.0, r.top),
142 					BPoint(r.right, r.top), black);
143 			AddLine(BPoint(r.right, r.top + 1.0),
144 					BPoint(r.right, r.top + handleBottomSize), black);
145 			AddLine(BPoint(r.right - 1.0, r.top + handleBottomSize + 1.0),
146 					BPoint(fKnobPos, r.top + handleArrowSize), black);
147 			AddLine(BPoint(fKnobPos - 1.0, r.top + handleArrowSize - 1.0),
148 					BPoint(r.left + 1.0, r.top + handleBottomSize + 1.0), black);
149 			// lower handle
150 			AddLine(BPoint(r.left, r.bottom),
151 					BPoint(r.left, r.bottom - handleBottomSize), black);
152 			AddLine(BPoint(r.left + 1.0, r.bottom - handleBottomSize - 1.0),
153 					BPoint(fKnobPos, r.bottom - handleArrowSize), black);
154 			AddLine(BPoint(fKnobPos + 1.0, r.bottom - handleArrowSize + 1.0),
155 					BPoint(r.right, r.bottom - handleBottomSize), black);
156 			AddLine(BPoint(r.right, r.bottom - handleBottomSize + 1.0),
157 					BPoint(r.right, r.bottom), black);
158 			AddLine(BPoint(r.right - 1.0, r.bottom),
159 					BPoint(r.left + 1.0, r.bottom), black);
160 		EndLineArray();
161 		// inner red light and shadow lines
162 		r.InsetBy(1.0, 1.0);
163 		handleBottomSize--;
164 		handleArrowSize -= 2.0;
165 		BeginLineArray(10);
166 			// upper handle
167 			AddLine(BPoint(r.left, r.top + handleBottomSize),
168 					BPoint(r.left, r.top), kSeekRedLight);
169 			AddLine(BPoint(r.left + 1.0, r.top),
170 					BPoint(r.right, r.top), kSeekRedLight);
171 			AddLine(BPoint(r.right, r.top + 1.0),
172 					BPoint(r.right, r.top + handleBottomSize), kSeekRedShadow);
173 			AddLine(BPoint(r.right - 1.0, r.top + handleBottomSize + 1.0),
174 					BPoint(fKnobPos, r.top + handleArrowSize), kSeekRedShadow);
175 			AddLine(BPoint(fKnobPos - 1.0, r.top + handleArrowSize - 1.0),
176 					BPoint(r.left + 1.0, r.top + handleBottomSize + 1.0), kSeekRedLight);
177 			// lower handle
178 			AddLine(BPoint(r.left, r.bottom),
179 					BPoint(r.left, r.bottom - handleBottomSize), kSeekRedLight);
180 			AddLine(BPoint(r.left + 1.0, r.bottom - handleBottomSize - 1.0),
181 					BPoint(fKnobPos, r.bottom - handleArrowSize), kSeekRedLight);
182 			AddLine(BPoint(fKnobPos + 1.0, r.bottom - handleArrowSize + 1.0),
183 					BPoint(r.right, r.bottom - handleBottomSize), kSeekRedShadow);
184 			AddLine(BPoint(r.right, r.bottom - handleBottomSize + 1.0),
185 					BPoint(r.right, r.bottom), kSeekRedShadow);
186 			AddLine(BPoint(r.right - 1.0, r.bottom),
187 					BPoint(r.left + 1.0, r.bottom), kSeekRedShadow);
188 		EndLineArray();
189 		// fill rest of handles with red
190 		SetHighColor(kSeekRed);
191 		r.InsetBy(1.0, 1.0);
192 		handleArrowSize -= 2.0;
193 		BPoint arrow[3];
194 		// upper handle arrow
195 		arrow[0].x = r.left;
196 		arrow[0].y = r.top;
197 		arrow[1].x = r.right;
198 		arrow[1].y = r.top;
199 		arrow[2].x = fKnobPos;
200 		arrow[2].y = r.top + handleArrowSize;
201 		FillPolygon(arrow, 3);
202 		// lower handle arrow
203 		arrow[0].x = r.left;
204 		arrow[0].y = r.bottom;
205 		arrow[1].x = r.right;
206 		arrow[1].y = r.bottom;
207 		arrow[2].x = fKnobPos;
208 		arrow[2].y = r.bottom - handleArrowSize;
209 		FillPolygon(arrow, 3);
210 	} else {
211 		// *** disabled look ***
212 		r.InsetBy(1.0, 1.0);
213 		_StrokeFrame(r, darkShadow, darkShadow, darkShadow, darkShadow);
214 		r.InsetBy(1.0, 1.0);
215 		_StrokeFrame(r, darkShadow, darkShadow, darkShadow, darkShadow);
216 		r.InsetBy(1.0, 1.0);
217 		SetHighColor(darkShadow);
218 		SetLowColor(shadow);
219 		// stripes
220 		float width = floorf(StringWidth(kDisabledSeekMessage));
221 		float textPos = r.left + r.Width() / 2.0 - width / 2.0;
222 		pattern stripes = { { 0xc7, 0x8f, 0x1f, 0x3e, 0x7c, 0xf8, 0xf1, 0xe3 } };
223 		BRect stripesRect(r);
224 		stripesRect.right = textPos - 5.0;
225 		FillRect(stripesRect, stripes);
226 		stripesRect.left = textPos + width + 3.0;
227 		stripesRect.right = r.right;
228 		FillRect(stripesRect, stripes);
229 		// info text
230 		r.left = textPos - 4.0;
231 		r.right = textPos + width + 2.0;
232 		FillRect(r);
233 		SetHighColor(shadow);
234 		SetLowColor(darkShadow);
235 		font_height fh;
236 		GetFontHeight(&fh);
237 		DrawString(kDisabledSeekMessage, BPoint(textPos, r.top + ceilf(fh.ascent) - 1.0));
238 	}
239 }
240 
241 
242 void
243 SeekSlider::MouseDown(BPoint where)
244 {
245 	if (IsEnabled() && Bounds().Contains(where)) {
246 		SetValue(_ValueFor(where.x));
247 		fTracking = true;
248 		SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
249 	}
250 }
251 
252 
253 void
254 SeekSlider::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
255 {
256 	if (fTracking) {
257 		SetValue(_ValueFor(where.x));
258 	}
259 }
260 
261 
262 void
263 SeekSlider::MouseUp(BPoint where)
264 {
265 	if (fTracking) {
266 		fTracking = false;
267 	}
268 }
269 
270 
271 
272 void
273 SeekSlider::ResizeToPreferred()
274 {
275 	float width = 15.0 + StringWidth(kDisabledSeekMessage) + 15.0;
276 	ResizeTo(width, 17.0);
277 }
278 
279 
280 void
281 SeekSlider::FrameResized(float width, float height)
282 {
283 	_SetKnobPosition(_KnobPosFor(Bounds(), Value()));
284 }
285 
286 
287 void
288 SeekSlider::SetPosition(float position)
289 {
290 	int32 value = fMinValue + (int32)floorf((fMaxValue - fMinValue) * position + 0.5);
291 	if (value != Value()) {
292 		BControl::SetValue(value);
293 		_SetKnobPosition(_KnobPosFor(Bounds(), Value()));
294 	}
295 }
296 
297 
298 bool
299 SeekSlider::IsTracking() const
300 {
301 	if (fTracking)
302 		return true;
303 	return system_time() - fLastTrackTime < 250000;
304 }
305 
306 
307 // #pragma mark -
308 
309 
310 int32
311 SeekSlider::_ValueFor(float xPos) const
312 {
313 	BRect r(Bounds());
314 	float knobWidth2 = SEEK_SLIDER_KNOB_WIDTH / 2.0;
315 	float sliderStart = (r.left + knobWidth2);
316 	float sliderEnd = (r.right - knobWidth2);
317 	int32 value =  fMinValue + (int32)(((xPos - sliderStart) * (fMaxValue - fMinValue))
318 				  / (sliderEnd - sliderStart - 1.0));
319 	if (value < fMinValue)
320 		value = fMinValue;
321 	if (value > fMaxValue)
322 		value = fMaxValue;
323 	return value;
324 }
325 
326 
327 int32
328 SeekSlider::_KnobPosFor(BRect r, int32 value) const
329 {
330 	float knobWidth2 = SEEK_SLIDER_KNOB_WIDTH / 2.0;
331 	r.left += knobWidth2;
332 	r.right -= knobWidth2;
333 	float knobPos = r.left
334 					+ floorf((r.right - r.left - 1.0) * (Value() - fMinValue)
335 					/ (fMaxValue - fMinValue) + 0.5);
336 	return (int32)knobPos;
337 }
338 
339 
340 void
341 SeekSlider::_StrokeFrame(BRect r, rgb_color left, rgb_color top,
342 						 rgb_color right, rgb_color bottom)
343 {
344 	BeginLineArray(4);
345 		AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), left);
346 		AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), top);
347 		AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), right);
348 		AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), bottom);
349 	EndLineArray();
350 }
351 
352 
353 void
354 SeekSlider::_SetKnobPosition(int32 knobPos)
355 {
356 	if (fKnobPos == knobPos)
357 		return;
358 
359 	float knobWidth2 = SEEK_SLIDER_KNOB_WIDTH / 2.0;
360 	BRect oldKnob(Bounds());
361 	BRect newKnob(oldKnob);
362 	oldKnob.left = fKnobPos - knobWidth2;
363 	oldKnob.right = fKnobPos + knobWidth2;
364 
365 	fKnobPos = knobPos;
366 
367 	newKnob.left = fKnobPos - knobWidth2;
368 	newKnob.right = fKnobPos + knobWidth2;
369 	Invalidate(oldKnob | newKnob);
370 }
371 
372