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