1 /* 2 * Copyright 2010, Stephan Aßmus <superstippi@gmx.de>. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "VolumeSlider.h" 8 9 #include <GradientLinear.h> 10 11 #include <stdio.h> 12 #include <string.h> 13 14 15 #define KNOB_EMBEDDED 0 16 #define ROUND_KNOB 0 17 18 static const rgb_color kGreen = (rgb_color){ 116, 224, 0, 255 }; 19 20 21 // constructor 22 VolumeSlider::VolumeSlider(const char* name, int32 minValue, int32 maxValue, 23 int32 snapValue, BMessage* message) 24 : 25 BSlider(name, NULL, NULL, minValue, maxValue, B_HORIZONTAL, 26 B_BLOCK_THUMB), 27 fMuted(false), 28 fSnapValue(snapValue), 29 fSnapping(false) 30 { 31 SetModificationMessage(message); 32 UseFillColor(true, &kGreen); 33 SetBarThickness(PreferredBarThickness()); 34 } 35 36 37 VolumeSlider::~VolumeSlider() 38 { 39 } 40 41 42 void 43 VolumeSlider::MouseMoved(BPoint where, uint32 transit, 44 const BMessage* dragMessage) 45 { 46 if (!IsTracking()) { 47 BSlider::MouseMoved(where, transit, dragMessage); 48 return; 49 } 50 51 float cursorPosition = Orientation() == B_HORIZONTAL ? where.x : where.y; 52 53 if (fSnapping 54 && cursorPosition >= fMinSnap && cursorPosition <= fMaxSnap) { 55 // Don't move the slider, keep the current value for a few 56 // more pixels 57 return; 58 } 59 60 fSnapping = false; 61 62 int32 oldValue = Value(); 63 int32 newValue = ValueForPoint(where); 64 if (oldValue == newValue) { 65 BSlider::MouseMoved(where, transit, dragMessage); 66 return; 67 } 68 69 // Check if there is a 0 dB transition at all 70 if ((oldValue < fSnapValue && newValue >= fSnapValue) 71 || (oldValue > fSnapValue && newValue <= fSnapValue)) { 72 SetValue(fSnapValue); 73 if (ModificationMessage() != NULL) 74 Messenger().SendMessage(ModificationMessage()); 75 76 float snapPoint = _PointForValue(fSnapValue); 77 const float kMaxSnapOffset = 6; 78 if (oldValue > newValue) { 79 // movement from right to left 80 fMinSnap = snapPoint - kMaxSnapOffset; 81 fMaxSnap = snapPoint + 1; 82 } else { 83 // movement from left to right 84 fMinSnap = snapPoint - 1; 85 fMaxSnap = snapPoint + kMaxSnapOffset; 86 } 87 88 fSnapping = true; 89 return; 90 } 91 92 BSlider::MouseMoved(where, transit, dragMessage); 93 } 94 95 96 BRect 97 VolumeSlider::ThumbFrame() const 98 { 99 #if !ROUND_KNOB 100 BRect rect = BSlider::ThumbFrame(); 101 rect.InsetBy(2, 2); 102 rect.bottom += 1; 103 #else 104 BRect rect(BarFrame()); 105 # if KNOB_EMBEDDED 106 // Knob embedded in bar frame 107 rect.InsetBy(0, 1); 108 # else 109 // Knob extends outside the bar frame 110 rect.InsetBy(0, -1); 111 # endif 112 rect.InsetBy(rect.Height() / 2, 0); 113 rect.left = rect.left + rect.Width() * Position() - rect.Height() / 2; 114 rect.right = rect.left + rect.Height(); 115 #endif 116 117 return rect; 118 } 119 120 121 void 122 VolumeSlider::DrawThumb() 123 { 124 #if ROUND_KNOB 125 // Draw a round thumb 126 BRect rect(ThumbFrame()); 127 128 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 129 rgb_color frameLightColor; 130 rgb_color frameShadowColor; 131 rgb_color shadowColor = (rgb_color){ 0, 0, 0, 60 }; 132 133 float topTint = 0.49; 134 float middleTint1 = 0.62; 135 float middleTint2 = 0.76; 136 float bottomTint = 0.90; 137 138 if (!IsEnabled()) { 139 topTint = (topTint + B_NO_TINT) / 2; 140 middleTint1 = (middleTint1 + B_NO_TINT) / 2; 141 middleTint2 = (middleTint2 + B_NO_TINT) / 2; 142 bottomTint = (bottomTint + B_NO_TINT) / 2; 143 shadowColor = (rgb_color){ 0, 0, 0, 30 }; 144 } 145 146 // Draw shadow 147 #if !KNOB_EMBEDDED 148 rect.left++; 149 rect.top++; 150 SetDrawingMode(B_OP_ALPHA); 151 SetHighColor(shadowColor); 152 FillEllipse(rect); 153 154 // Draw thumb shape 155 rect.OffsetBy(-1, -1); 156 #endif 157 158 if (IsFocus()) { 159 // focused 160 frameLightColor = ui_color(B_KEYBOARD_NAVIGATION_COLOR); 161 frameShadowColor = frameLightColor; 162 } else { 163 // figure out the tints to be used 164 float frameLightTint; 165 float frameShadowTint; 166 167 if (!IsEnabled()) { 168 frameLightTint = 1.30; 169 frameShadowTint = 1.35; 170 shadowColor.alpha = 30; 171 } else { 172 frameLightTint = 1.6; 173 frameShadowTint = 1.65; 174 } 175 176 frameLightColor = tint_color(base, frameLightTint); 177 frameShadowColor = tint_color(base, frameShadowTint); 178 } 179 180 BGradientLinear frameGradient; 181 frameGradient.AddColor(frameShadowColor, 0); 182 frameGradient.AddColor(frameLightColor, 255); 183 frameGradient.SetStart(rect.LeftTop()); 184 frameGradient.SetEnd(rect.RightBottom()); 185 186 FillEllipse(rect, frameGradient); 187 rect.InsetBy(1, 1); 188 189 // frameGradient.MakeEmpty(); 190 // frameGradient.AddColor(borderColor, 0); 191 // frameGradient.AddColor(tint_color(borderColor, 0.8), 255); 192 // view->FillEllipse(rect, frameGradient); 193 // rect.InsetBy(1, 1); 194 195 BGradientLinear gradient; 196 if (!IsEnabled()) { 197 gradient.AddColor(tint_color(base, topTint), 0); 198 gradient.AddColor(tint_color(base, bottomTint), 255); 199 } else { 200 gradient.AddColor(tint_color(base, topTint), 0); 201 gradient.AddColor(tint_color(base, middleTint1), 132); 202 gradient.AddColor(tint_color(base, middleTint2), 136); 203 gradient.AddColor(tint_color(base, bottomTint), 255); 204 } 205 gradient.SetStart(rect.LeftTop()); 206 gradient.SetEnd(rect.LeftBottom()); 207 FillEllipse(rect, gradient); 208 #else 209 BSlider::DrawThumb(); 210 #endif 211 } 212 213 214 BSize 215 VolumeSlider::MinSize() 216 { 217 BSize size = BSlider::MinSize(); 218 size.width *= 2; 219 return size; 220 } 221 222 223 void 224 VolumeSlider::SetMuted(bool mute) 225 { 226 if (mute == fMuted) 227 return; 228 229 fMuted = mute; 230 231 rgb_color fillColor = kGreen; 232 if (fMuted) { 233 fillColor = tint_color(ui_color(B_PANEL_BACKGROUND_COLOR), 234 B_DARKEN_2_TINT); 235 } 236 237 UseFillColor(true, &fillColor); 238 239 Invalidate(); 240 } 241 242 243 float 244 VolumeSlider::PreferredBarThickness() const 245 { 246 #if KNOB_EMBEDDED 247 return 10.0f; 248 #else 249 return 8.0f; 250 #endif 251 } 252 253 254 float 255 VolumeSlider::_PointForValue(int32 value) const 256 { 257 int32 min, max; 258 GetLimits(&min, &max); 259 260 if (Orientation() == B_HORIZONTAL) { 261 return ceilf(1.0f * (value - min) / (max - min) 262 * (BarFrame().Width() - 2) + BarFrame().left + 1); 263 } 264 265 return ceilf(BarFrame().top - 1.0f * (value - min) / (max - min) 266 * BarFrame().Height()); 267 } 268 269 270