1/* 2 * Copyright 2006-2012 Stephan A��mus <superstippi@gmx.de> 3 * Distributed under the terms of the MIT License. 4 */ 5 6#include "AlphaSlider.h" 7 8#include <stdio.h> 9#include <string.h> 10 11#include <AppDefs.h> 12#include <Bitmap.h> 13#include <ControlLook.h> 14#include <LayoutUtils.h> 15#include <Message.h> 16#include <Window.h> 17 18#include "ui_defines.h" 19 20// constructor 21AlphaSlider::AlphaSlider(orientation dir, BMessage* message, 22 border_style border) 23 : BControl("alpha slider", NULL, message, 24 B_WILL_DRAW | B_FRAME_EVENTS | B_NAVIGABLE) 25 , fBitmap(NULL) 26 , fColor(kBlack) 27 , fDragging(false) 28 , fOrientation(dir) 29 , fBorderStyle(border) 30{ 31 FrameResized(Bounds().Width(), Bounds().Height()); 32 33 SetViewColor(B_TRANSPARENT_COLOR); 34 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 35 36 SetValue(255); 37} 38 39// destructor 40AlphaSlider::~AlphaSlider() 41{ 42 delete fBitmap; 43} 44 45// WindowActivated 46void 47AlphaSlider::WindowActivated(bool active) 48{ 49 if (IsFocus()) 50 Invalidate(); 51} 52 53// MakeFocus 54void 55AlphaSlider::MakeFocus(bool focus) 56{ 57 if (focus != IsFocus()) { 58 BControl::MakeFocus(focus); 59 Invalidate(); 60 } 61} 62 63// MinSize 64BSize 65AlphaSlider::MinSize() 66{ 67 BSize minSize; 68 if (fOrientation == B_HORIZONTAL) 69 minSize = BSize(255 + 4, 7 + 4); 70 else 71 minSize = BSize(7 + 4, 255 + 4); 72 return BLayoutUtils::ComposeSize(ExplicitMinSize(), minSize); 73} 74 75// PreferredSize 76BSize 77AlphaSlider::PreferredSize() 78{ 79 return BLayoutUtils::ComposeSize(ExplicitPreferredSize(), MinSize()); 80} 81 82// MaxSize 83BSize 84AlphaSlider::MaxSize() 85{ 86 BSize minSize; 87 if (fOrientation == B_HORIZONTAL) 88 minSize = BSize(B_SIZE_UNLIMITED, 16 + 4); 89 else 90 minSize = BSize(16 + 4, B_SIZE_UNLIMITED); 91 return BLayoutUtils::ComposeSize(ExplicitMinSize(), minSize); 92} 93 94// MouseDown 95void 96AlphaSlider::MouseDown(BPoint where) 97{ 98 if (!IsEnabled()) 99 return; 100 101// if (!IsFocus()) 102// MakeFocus(true); 103 104 fDragging = true; 105 SetValue(_ValueFor(where)); 106 107 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS); 108} 109 110// MouseUp 111void 112AlphaSlider::MouseUp(BPoint where) 113{ 114 fDragging = false; 115} 116 117// MouseMoved 118void 119AlphaSlider::MouseMoved(BPoint where, uint32 transit, 120 const BMessage* dragMessage) 121{ 122 if (!IsEnabled() || !fDragging) 123 return; 124 125 SetValue(_ValueFor(where)); 126} 127 128// KeyDown 129void 130AlphaSlider::KeyDown(const char* bytes, int32 numBytes) 131{ 132 if (!IsEnabled() || numBytes <= 0) { 133 BControl::KeyDown(bytes, numBytes); 134 return; 135 } 136 137 switch (bytes[0]) { 138 case B_HOME: 139 SetValue(0); 140 break; 141 case B_END: 142 SetValue(255); 143 break; 144 145 case B_LEFT_ARROW: 146 case B_UP_ARROW: 147 SetValue(max_c(0, Value() - 1)); 148 break; 149 case B_RIGHT_ARROW: 150 case B_DOWN_ARROW: 151 SetValue(min_c(255, Value() + 1)); 152 break; 153 154 default: 155 BControl::KeyDown(bytes, numBytes); 156 break; 157 } 158} 159 160// Draw 161void 162AlphaSlider::Draw(BRect updateRect) 163{ 164 BRect b = Bounds(); 165 bool isFocus = IsFocus() && Window()->IsActive(); 166 167 if (fBorderStyle == B_FANCY_BORDER) { 168 rgb_color bg = LowColor(); 169 uint32 flags = 0; 170 171 if (!IsEnabled()) 172 flags |= BControlLook::B_DISABLED; 173 if (isFocus) 174 flags |= BControlLook::B_FOCUSED; 175 176 be_control_look->DrawTextControlBorder(this, b, updateRect, bg, flags); 177 } 178 179 DrawBitmap(fBitmap, b.LeftTop()); 180 181 // value marker 182 if (fOrientation == B_HORIZONTAL) { 183 float pos = floorf(b.left + Value() * b.Width() / 255.0 + 0.5); 184 185 if (pos - 2 >= b.left) { 186 SetHighColor(kWhite); 187 StrokeLine(BPoint(pos - 2, b.top), BPoint(pos - 2, b.bottom)); 188 } 189 if (pos - 1 >= b.left) { 190 SetHighColor(kBlack); 191 StrokeLine(BPoint(pos - 1, b.top), BPoint(pos - 1, b.bottom)); 192 } 193 if (pos + 1 <= b.right) { 194 SetHighColor(kBlack); 195 StrokeLine(BPoint(pos + 1, b.top), BPoint(pos + 1, b.bottom)); 196 } 197 if (pos + 2 <= b.right) { 198 SetHighColor(kWhite); 199 StrokeLine(BPoint(pos + 2, b.top), BPoint(pos + 2, b.bottom)); 200 } 201 } else { 202 float pos = floorf(b.top + Value() * b.Height() / 255.0 + 0.5); 203 204 if (pos - 2 >= b.top) { 205 SetHighColor(kWhite); 206 StrokeLine(BPoint(b.left, pos - 2), BPoint(b.right, pos - 2)); 207 } 208 if (pos - 1 >= b.top) { 209 SetHighColor(kBlack); 210 StrokeLine(BPoint(b.left, pos - 1), BPoint(b.right, pos - 1)); 211 } 212 if (pos + 1 <= b.bottom) { 213 SetHighColor(kBlack); 214 StrokeLine(BPoint(b.left, pos + 1), BPoint(b.right, pos + 1)); 215 } 216 if (pos + 2 <= b.bottom) { 217 SetHighColor(kWhite); 218 StrokeLine(BPoint(b.left, pos + 2), BPoint(b.right, pos + 2)); 219 } 220 } 221} 222 223// FrameResized 224void 225AlphaSlider::FrameResized(float width, float height) 226{ 227 BRect r = _BitmapRect(); 228 _AllocBitmap(r.IntegerWidth() + 1, r.IntegerHeight() + 1); 229 _UpdateColors(); 230 Invalidate(); 231 232} 233 234// SetValue 235void 236AlphaSlider::SetValue(int32 value) 237{ 238 if (value == Value()) 239 return; 240 241 Invoke(Message()); 242 BControl::SetValue(value); 243} 244 245// SetEnabled 246void 247AlphaSlider::SetEnabled(bool enabled) 248{ 249 if (enabled == IsEnabled()) 250 return; 251 252 BControl::SetEnabled(enabled); 253 254 _UpdateColors(); 255 Invalidate(); 256} 257 258// #pragma mark - 259 260// SetColor 261void 262AlphaSlider::SetColor(const rgb_color& color) 263{ 264 if ((uint32&)fColor == (uint32&)color) 265 return; 266 267 fColor = color; 268 269 _UpdateColors(); 270 Invalidate(); 271} 272 273// #pragma mark - 274 275// blend_colors 276inline void 277blend_colors(uint8* d, uint8 alpha, uint8 c1, uint8 c2, uint8 c3) 278{ 279 if (alpha > 0) { 280 if (alpha == 255) { 281 d[0] = c1; 282 d[1] = c2; 283 d[2] = c3; 284 } else { 285 d[0] += (uint8)(((c1 - d[0]) * alpha) >> 8); 286 d[1] += (uint8)(((c2 - d[1]) * alpha) >> 8); 287 d[2] += (uint8)(((c3 - d[2]) * alpha) >> 8); 288 } 289 } 290} 291 292// _UpdateColors 293void 294AlphaSlider::_UpdateColors() 295{ 296 if (fBitmap == NULL || !fBitmap->IsValid()) 297 return; 298 299 // fill in top row with alpha gradient 300 uint8* topRow = (uint8*)fBitmap->Bits(); 301 uint32 width = fBitmap->Bounds().IntegerWidth() + 1; 302 303 uint8* p = topRow; 304 rgb_color color = fColor; 305 if (!IsEnabled()) { 306 // blend low color and color to give disabled look 307 rgb_color bg = LowColor(); 308 color.red = (uint8)(((uint32)bg.red + color.red) / 2); 309 color.green = (uint8)(((uint32)bg.green + color.green) / 2); 310 color.blue = (uint8)(((uint32)bg.blue + color.blue) / 2); 311 } 312 for (uint32 x = 0; x < width; x++) { 313 p[0] = color.blue; 314 p[1] = color.green; 315 p[2] = color.red; 316 p[3] = x * 255 / width; 317 p += 4; 318 } 319 // copy top row to rest of bitmap 320 uint32 height = fBitmap->Bounds().IntegerHeight() + 1; 321 uint32 bpr = fBitmap->BytesPerRow(); 322 uint8* dstRow = topRow + bpr; 323 for (uint32 i = 1; i < height; i++) { 324 memcpy(dstRow, topRow, bpr); 325 dstRow += bpr; 326 } 327 // post process bitmap to underlay it with a pattern to visualize alpha 328 uint8* row = topRow; 329 for (uint32 i = 0; i < height; i++) { 330 uint8* p = row; 331 for (uint32 x = 0; x < width; x++) { 332 uint8 alpha = p[3]; 333 if (alpha < 255) { 334 p[3] = 255; 335 alpha = 255 - alpha; 336 if (x % 8 >= 4) { 337 if (i % 8 >= 4) { 338 blend_colors(p, alpha, 339 kAlphaLow.blue, 340 kAlphaLow.green, 341 kAlphaLow.red); 342 } else { 343 blend_colors(p, alpha, 344 kAlphaHigh.blue, 345 kAlphaHigh.green, 346 kAlphaHigh.red); 347 } 348 } else { 349 if (i % 8 >= 4) { 350 blend_colors(p, alpha, 351 kAlphaHigh.blue, 352 kAlphaHigh.green, 353 kAlphaHigh.red); 354 } else { 355 blend_colors(p, alpha, 356 kAlphaLow.blue, 357 kAlphaLow.green, 358 kAlphaLow.red); 359 } 360 } 361 } 362 p += 4; 363 } 364 row += bpr; 365 } 366} 367 368// _AllocBitmap 369void 370AlphaSlider::_AllocBitmap(int32 width, int32 height) 371{ 372 if (width < 2 || height < 2) 373 return; 374 375 delete fBitmap; 376 fBitmap = new BBitmap(BRect(0, 0, width - 1, height - 1), 0, B_RGB32); 377} 378 379// _BitmapRect 380BRect 381AlphaSlider::_BitmapRect() const 382{ 383 BRect r = Bounds(); 384 if (fBorderStyle == B_FANCY_BORDER) 385 r.InsetBy(2, 2); 386 return r; 387} 388 389// _ValueFor 390int32 391AlphaSlider::_ValueFor(BPoint where) const 392{ 393 BRect r = _BitmapRect(); 394 395 int32 value; 396 if (fOrientation == B_HORIZONTAL) 397 value = (int32)(255 * (where.x - r.left) / r.Width() + 0.5); 398 else 399 value = (int32)(255 * (where.y - r.top) / r.Height() + 0.5); 400 401 value = max_c(0, value); 402 value = min_c(255, value); 403 404 return value; 405} 406 407