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