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